Quick answer: The prefab is not in the NetworkManager's NetworkPrefabs list. Every prefab that will be spawned over the network must be registered in the NetworkManager's prefab list before starting the host or client. Add the prefab to the list in the Inspector or via NetworkManager.
Here is how to fix Unity multiplayer netcode object not spawning. You call Spawn() on a NetworkObject and nothing happens. No error, no object on the client, or an error about unregistered prefabs. Netcode for GameObjects requires a precise setup before any object can be replicated across the network, and missing any step in that chain results in spawns that silently fail or throw cryptic exceptions.
How Network Spawning Works
In Unity's Netcode for GameObjects (NGO), network spawning is fundamentally different from local instantiation. When the server calls Spawn() on a NetworkObject, it assigns a network ID, serializes the object's state, and sends a spawn message to all connected clients. Each client receives the message, looks up the prefab in its local NetworkPrefabs list by hash, instantiates a local copy, and applies the serialized state.
Every link in this chain must work for spawning to succeed: the prefab must have a NetworkObject component, it must be registered in the NetworkManager's prefab list on both server and client, the spawn call must originate from the server, and the NetworkManager must be running. A failure at any point produces a different symptom, but the visible result is the same: the object does not appear.
Registering Prefabs with NetworkManager
The most common spawn failure is an unregistered prefab. The NetworkManager maintains a list of prefabs that can be spawned over the network. If a prefab is not in this list, the server cannot spawn it and clients cannot recognize incoming spawn messages for it.
using Unity.Netcode;
using UnityEngine;
public class PrefabRegistrar : MonoBehaviour
{
[SerializeField] private GameObject[] _networkPrefabs;
void Awake()
{
var manager = NetworkManager.Singleton;
if (manager == null)
{
Debug.LogError("NetworkManager.Singleton is null");
return;
}
foreach (var prefab in _networkPrefabs)
{
if (prefab.GetComponent<NetworkObject>() == null)
{
Debug.LogError($"{prefab.name} missing NetworkObject component");
continue;
}
manager.AddNetworkPrefab(prefab);
Debug.Log($"Registered network prefab: {prefab.name}");
}
}
}
You can also register prefabs in the Inspector by selecting the NetworkManager GameObject and adding entries to the NetworkPrefabs list. The Inspector approach is simpler but becomes unwieldy with many prefabs. The runtime approach shown above lets you organize prefabs in arrays or ScriptableObjects and register them programmatically.
Registration must happen before calling StartHost(), StartServer(), or StartClient(). Registering after the network session starts will not work for prefabs that need to be spawned during the initial connection sync.
Server-Only Spawning
In Netcode for GameObjects, only the server (or host) can spawn NetworkObjects. If a client tries to call Spawn(), it either throws an exception or does nothing, depending on the version. This is by design — the server is the authority on what exists in the networked world.
When a client needs to spawn something (like a projectile when the player fires), it must request the spawn via a ServerRpc.
using Unity.Netcode;
using UnityEngine;
public class ProjectileSpawner : NetworkBehaviour
{
[SerializeField] private GameObject _projectilePrefab;
[SerializeField] private Transform _firePoint;
void Update()
{
if (!IsOwner) return;
if (Input.GetButtonDown("Fire1"))
{
// Client requests the server to spawn the projectile
SpawnProjectileServerRpc(_firePoint.position, _firePoint.rotation);
}
}
[ServerRpc]
private void SpawnProjectileServerRpc(Vector3 position, Quaternion rotation)
{
var projectile = Instantiate(_projectilePrefab, position, rotation);
var netObj = projectile.GetComponent<NetworkObject>();
netObj.Spawn();
}
}
The pattern is always the same: the client detects the intent (input, trigger, timer), sends a ServerRpc with the necessary parameters, and the server performs the actual spawn. The spawned object is automatically replicated to all clients by Netcode.
SpawnWithOwnership and Client Authority
By default, spawned objects are owned by the server. If a client needs authority over the spawned object (for example, a player's character or a vehicle they are driving), use SpawnWithOwnership or SpawnAsPlayerObject.
[ServerRpc]
private void RequestVehicleServerRpc(ServerRpcParams rpcParams = default)
{
var clientId = rpcParams.Receive.SenderClientId;
var vehicle = Instantiate(_vehiclePrefab);
var netObj = vehicle.GetComponent<NetworkObject>();
// Spawn with client ownership so they can control it
netObj.SpawnWithOwnership(clientId);
Debug.Log($"Spawned vehicle for client {clientId}");
}
// Transfer ownership later if needed
public void TransferOwnership(NetworkObject netObj, ulong newOwnerId)
{
if (!IsServer) return;
netObj.ChangeOwnership(newOwnerId);
}
A common mistake is to spawn with server ownership and then wonder why the client's IsOwner checks fail. If the client needs to send RPCs or write to NetworkVariables on the object, it must own it. Use SpawnWithOwnership at spawn time or ChangeOwnership later.
Never use
Object.Instantiatealone for objects that should exist on other clients. A locally instantiated object is invisible to the network. You must callNetworkObject.Spawn()on the server after instantiating to make it part of the networked world.
NetworkManager.Singleton and Timing
Many spawn failures happen because code runs before the NetworkManager is ready. NetworkManager.Singleton is set in Awake, but if your script's Awake runs first, the singleton is null. Similarly, IsServer and IsClient are only valid after the network session starts.
public class GameSpawner : NetworkBehaviour
{
[SerializeField] private GameObject _enemyPrefab;
public override void OnNetworkSpawn()
{
// This is the safe place to do network operations
if (IsServer)
{
SpawnInitialEnemies();
}
}
private void SpawnInitialEnemies()
{
for (int i = 0; i < 5; i++)
{
var pos = new Vector3(Random.Range(-10f, 10f), 0, Random.Range(-10f, 10f));
var enemy = Instantiate(_enemyPrefab, pos, Quaternion.identity);
enemy.GetComponent<NetworkObject>().Spawn();
}
}
}
Use OnNetworkSpawn() instead of Start() or Awake() for any code that depends on the network being active. This callback fires after the object has been spawned on the network, guaranteeing that IsServer, IsClient, IsOwner, and NetworkManager.Singleton are all valid.
Debugging Spawn Failures
When spawning fails silently, add logging at each step to identify where the chain breaks.
public void DebugSpawn(GameObject prefab, Vector3 position)
{
// Step 1: Check NetworkManager
if (NetworkManager.Singleton == null)
{
Debug.LogError("NetworkManager.Singleton is null");
return;
}
// Step 2: Check if we are the server
if (!NetworkManager.Singleton.IsServer)
{
Debug.LogError("Only the server can spawn objects");
return;
}
// Step 3: Check NetworkObject component
var netObj = prefab.GetComponent<NetworkObject>();
if (netObj == null)
{
Debug.LogError($"{prefab.name} has no NetworkObject component");
return;
}
// Step 4: Instantiate and spawn
var instance = Instantiate(prefab, position, Quaternion.identity);
var instanceNet = instance.GetComponent<NetworkObject>();
Debug.Log($"Spawning {prefab.name} at {position}");
instanceNet.Spawn();
Debug.Log($"Spawned with NetworkObjectId: {instanceNet.NetworkObjectId}");
}
Enable Netcode's built-in logging by setting the log level on the NetworkManager. In development, use LogLevel.Developer to see every network message, including spawn/despawn events, RPC calls, and connection state changes. This reveals whether spawn messages are being sent but not received, or not sent at all.
Related Issues
If spawned objects appear but with wrong or missing data, see serialized field lost after renaming. If the spawned prefab was loaded via Addressables and fails, check Addressables failed to load. For camera jitter when following network-spawned players, see Cinemachine camera jittering.
If you call Instantiate without Spawn, the object only exists on the machine that created it. Every other client has no idea it is there.