the symptom, in your words

"Null check on a destroyed GameObject still NREs — use IsValid()"

✓ verified on engine 26.07lane: Writing gameplayposted

▸ SYMPTOM

A per-frame consumer (camera, HUD, AI controller) holds a cached reference to a Component or GameObject and guards it with if (Target == null) return;. After the target is destroyed — typically during session teardown when a disconnected client's pawn is torn down — the guard passes but accessing any member (e.g. Target.WorldPosition) throws a NullReferenceException every frame, flooding the console with "Exception when calling 'Update' on ..." errors.

▸ CAUSE

s&box overloads the == operator on GameObject and Component so that obj == null returns true only for truly null references. A destroyed object is not null in the CLR sense — it is a dead managed wrapper whose backing native object has been freed. The == null check does not detect this state; it passes, and any property access on the zombie wrapper throws.

IsValid() checks both nullity and the alive/destroyed flag, catching both cases.

▸ FIX

Replace == null guards with IsValid() on any reference to a destroyable object — especially per-frame consumers of networked objects (cameras, HUD bindings, AI targets):

snippet
// In OnUpdate / OnFixedUpdate:
if ( !Target.IsValid() )
{
    Target = FindNewTarget();  // or re-acquire
}
if ( !Target.IsValid() )
    return;

// Now safe to access Target.WorldPosition, etc.

Audit all per-frame consumers that hold references to objects that can be destroyed externally (networked pawns, pooled objects, scene-transition survivors). A single stale == null guard in one file can cause continuous NRE spam when the network state changes.

▸ WHY IT WORKS

IsValid() is the engine's canonical liveness check. It returns false for null references, destroyed objects, and disabled components — the complete set of "this reference should not be used." The == null operator only covers the first case. Session teardown, network disconnects, and scene transitions all destroy objects without nulling every external reference, so IsValid() is the only safe guard for cached references to objects you don't own the lifetime of.

Verified on engine 26.07 — seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?