FiveM Entity Ownership and Network IDs: Spawning, Syncing and Deleting Objects Without Desync
Most “the prop isn’t there for everyone” and “the car deleted itself” bugs come down to one misunderstood concept: fivem entity ownership. Under OneSync, every networked entity has exactly one client that owns it at any given moment, and that owner is the only machine actually simulating the entity’s physics and authoritative state. Ignore who owns an entity and how it is addressed across the network and you get props that exist for one player and not another, vehicles that snap back to old positions, and orphaned entities that pile up until the server stutters. This guide covers ownership, network IDs, spawning, and clean deletion so your objects stay in sync.
What Entity Ownership Actually Means
In a OneSync session the server is the source of truth for which entities exist, but it delegates simulation. The owning client runs the physics, decides position, velocity, and door/state, then streams that to the server, which relays it to everyone else. Ownership is not fixed: it migrates. If a player walks away and another gets closer, the engine can hand ownership to the nearer client because they are better positioned to simulate it. This is why a setter you call on a client may silently do nothing, the client running your code does not own that entity, so its writes are not authoritative and get overwritten by the real owner.
The practical rule: state changes only stick when applied by the owner, or on the server. Calling SetEntityCoords on a vehicle you do not own will fight the owner and visibly rubber-band.
Network IDs vs Entity Handles
An entity handle (the number returned by CreateObject or GetVehiclePedIsIn) is a local value, only meaningful on the machine that produced it. The same prop has a different handle on every client and on the server, so you can never send a handle to another client and expect it to refer to anything.
The portable identifier is the network ID. Convert with NetworkGetNetworkIdFromEntity(entity) before you send it across the wire, and resolve it back on the receiving side with NetworkGetEntityFromNetworkId(netId). The net ID is consistent server-wide, so a client receiving it can look up its own local handle for that same networked entity.
- Sync the net ID in events, never the raw handle.
- On receipt, resolve with
NetworkGetEntityFromNetworkId, then guard withDoesEntityExistbefore touching it, the entity may not have streamed in yet. - On the server, validate that a net ID a client sent actually maps to a real entity before acting on it.
Spawning Objects the Right Way
Client-side spawning follows a fixed sequence: load the model, create the entity, then mark it so the engine does not clean it up.
- Hash and request the model:
RequestModel(hash), then loop withWait(0)untilHasModelLoaded(hash)returns true. - Create the entity:
CreateObject(hash, x, y, z, isNetwork, netMissionEntity, doorFlag)for props, orCreateVehiclefor vehicles. PassisNetwork = trueif you want it networked and visible to others. - Call
SetEntityAsMissionEntity(entity, true, true)so the population/cleanup system does not despawn it when you look away. - Free the model with
SetModelAsNoLongerNeeded(hash)once the entity exists.
If you skip SetEntityAsMissionEntity, the engine treats the object as ambient and may delete it during a population pass, which is a classic “my spawned car vanished” report.
Client-Created vs Server-Created Entities
Where you create an entity changes who owns it and how reliably it syncs. A client-created networked entity is owned by the creating client at birth. That is fine for transient, local-ish props, but ownership can migrate the moment that player disconnects or moves, and if they crash before the entity registers, it may never appear for others.
Server-created entities are more robust for anything persistent. On the server you use CreateObjectNoOffset(model, x, y, z, isNetwork, netMissionEntity, doorFlag) (and CreateVehicleServerSetter / CreatePed equivalents). The server owns the entity until it assigns an owner, so it exists authoritatively before any client streams it in. Server-side setters such as SetEntityCoords and SetVehicleNumberPlateText apply without an owning client present. For shared world objects, server creation avoids the “appears for the spawner only” failure mode entirely.
Requesting and Controlling Ownership
When a client genuinely needs to manipulate an entity it does not own, it must request control first:
- Call
NetworkRequestControlOfEntity(entity)in a short loop, yielding withWait(0), untilNetworkHasControlOfEntity(entity)returns true (or you time out). Control is not instant; the request travels to the current owner. - For entities that should not change hands mid-operation, call
SetNetworkIdCanMigrate(netId, false)to lock ownership so a manipulation in progress is not interrupted. - Once you are done and want normal behavior back, re-enable migration so the engine can rebalance simulation.
Forgetting to take control is the number one reason a setter “does nothing”: the call succeeds locally but is discarded because you are not the owner.
Why Entities Desync or Appear for Only One Player
Desync almost always traces to a few causes: sending a local handle instead of a net ID; writing state from a non-owner; spawning with isNetwork = false so the entity is purely local and never replicated; or touching an entity before it has streamed in. Streaming distance matters too, an entity far outside a player’s focus may not be instanced for them yet, so DoesEntityExist returns false there even though it is alive elsewhere.
Deleting Entities Cleanly
Deletion follows the same ownership rule: you generally must own an entity (or be the server) to delete it. The safe pattern is guard, take control, then delete.
- Check
DoesEntityExist(entity)first, deleting a stale handle throws or no-ops unpredictably. - Take control with
NetworkRequestControlOfEntityif you are a client and not the owner. - Call
SetEntityAsMissionEntity(entity, true, true)thenDeleteEntity(entity). Marking it as a mission entity first makesDeleteEntityreliable.
Orphaned entities accumulate when scripts spawn objects but never track their net IDs, so on resource stop or player disconnect nothing deletes them. Keep a table of every net ID you spawn, delete them in your resource’s onResourceStop handler, and clean up per-player entities when that player drops. Prefer server-side deletion for shared objects so one authoritative call removes the entity for everyone.
Get ownership and net IDs right and the rest of your networking gets dramatically simpler. For ready-made QBCore resources that already handle spawning and cleanup correctly, browse qb-tebex.io; for a wider catalog of standalone and framework scripts see cfx-tebex.store; and if entity leaks are dragging your frame time down, the monitoring tooling at 0resmon-tebex.io helps you spot which resource is leaking before it becomes a crash.