Skip to main content

Entity validity

The single most common bug in GTA V modding is attempting to read or manipulate an entity that no longer exists in the world. In pure C#, checking if (myPed != null) is enough to know whether an object is allocated in memory. In GTA V modding, that check is dangerously incomplete.

The stale handle trap

When you create an entity, SHVDN wraps the engine handle inside a C# object:

private Ped _bodyguard;

private void SpawnGuard()
{
_bodyguard = World.CreatePed(model, spawnPos);
}

Now imagine what happens three minutes later:

  1. The bodyguard gets into a shootout and dies.
  2. An ambulance or the ambient cleaner removes the corpse.
  3. The engine frees the handle.

In C#, _bodyguard is still non-null! Your variable still holds a reference to the GTA.Ped instance wrapping the old handle. But in the game, that handle is dead. If you now call _bodyguard.Position, SHVDN asks the engine for the position of a non-existent entity, and your script crashes.

The double check: null and Exists()

Whenever you touch an entity stored across multiple frames, you must check both C# reference nullity and native engine existence:

if (_bodyguard != null && _bodyguard.Exists())
{
// The entity is real and alive in the engine memory pool
Vector3 guardPos = _bodyguard.Position;
}

Exists() calls the native function DOES_ENTITY_EXIST under the hood. It checks whether the handle is currently active in the engine's pool.

Dead vs invalid

Do not confuse an invalid entity with a dead entity:

  • Dead (ped.IsDead): The ped's health is zero. The physical body (ragdoll or corpse) is still lying on the street. Its handle is still valid. You can read its position, check its wounds, or revive it.
  • Invalid (!ped.Exists()): The entity is completely gone from the game world. It was despawned, deleted, or never spawned at all. Any attempt to call methods on it fails.

Here is the defensive pattern used in production mods:

private void OnTick(object sender, EventArgs e)
{
// 1. Guard against invalid entity
if (_bodyguard == null || !_bodyguard.Exists())
{
return;
}

// 2. Guard against dead entity
if (_bodyguard.IsDead)
{
GTA.UI.Notification.PostTicker("Your bodyguard was eliminated.", false);
_bodyguard = null;
return;
}

// 3. Safe to execute live logic
_bodyguard.Task.FollowToOffsetFromEntity(Game.Player.Character, new Vector3(0f, -2f, 0f));
}

In the next lesson, we look at models: loading them into video memory, verifying their readiness, and releasing them cleanly.