Quick answer: MultiplayerSpawner replicates only children added under its configured spawn_path, and only if the scene is listed in _spawnable_scenes. The multiplayer peer must be set on both server and client before add_child is called on the spawn parent, otherwise the spawn message is dropped.

Here is how to fix Godot 4 MultiplayerSpawner that adds children on the server but never spawns them on clients. You add a player scene under the spawn_path on the server, the player appears locally, but client peers see an empty world. The fix involves understanding spawn_path strict-matching, the _spawnable_scenes whitelist, and the timing of peer registration.

The Symptom

Server starts a session. multiplayer.peer = ENetMultiplayerPeer.create_server(...) succeeds. Server adds child scenes under what should be the spawn parent. The server sees them. Connecting clients show the world without any of those children. Console shows no errors.

What Causes This

Wrong spawn_path target. MultiplayerSpawner replicates children of exactly the node referenced by spawn_path. Adding under a parent of that path or a sibling does nothing.

Scene not in spawnable list. The spawner whitelists scenes via _spawnable_scenes. Spawning a non-listed PackedScene is silently ignored.

Spawned before peer registered. If the server adds children before clients connect, those children are not retroactively replicated unless the spawner is configured for that. New clients see only future spawns.

Authority mismatch on spawn parent. The MultiplayerSpawner’s authority is the server (1) by default. Adding from a client violates the authority model and is rejected.

The Fix

Step 1: Configure the spawner correctly.

# In the editor on the MultiplayerSpawner node:
#   Spawn Path = ../World/Players
#   Spawnable Scenes = [res://player.tscn, res://npc.tscn]

Verify the spawn path resolves at runtime — if your scene tree changes, a stale path silently fails.

Step 2: Spawn from the server.

extends Node

@onready var spawner: MultiplayerSpawner = $MultiplayerSpawner
@onready var players_root: Node = $World/Players

func _on_peer_connected(id: int):
    if not multiplayer.is_server():
        return

    var player = preload("res://player.tscn").instantiate()
    player.name = str(id)              # Unique node names per client
    player.set_multiplayer_authority(id)
    players_root.add_child(player, true)

The set_multiplayer_authority(id) call assigns the client peer ownership so input from that client replicates correctly back to the server’s copy.

Step 3: Connect the peer signals before starting.

func _ready():
    multiplayer.peer_connected.connect(_on_peer_connected)
    multiplayer.peer_disconnected.connect(_on_peer_disconnected)

Step 4: Use _spawn_custom for parameterized spawns. If your spawn needs initialization data, override _spawn_custom:

func _spawn_custom(data: Variant) -> Node:
    var n = preload("res://enemy.tscn").instantiate()
    n.position = data["pos"]
    n.health  = data["hp"]
    return n

# On server
spawner.spawn({ "pos": Vector2(100, 0), "hp": 50 })

The Variant is sent to clients, which call _spawn_custom with the same data to recreate the node.

Step 5: Verify late-join replication. Without explicit handling, clients joining after a spawn will not see existing children. Enable Spawn All Existing on the MultiplayerSpawner if you want clients to receive everything on connect, or write your own catch-up code that re-spawns on peer_connected.

Debugging Tips

Add prints on both server and client around spawn time:

print("Server: adding child ", player.name, " under ", players_root.get_path())
print("Client: children of players_root: ", players_root.get_children())

If the server log shows the child but the client log does not, the spawn message did not replicate. Common causes: spawn_path mismatch, missing scene in spawnable list, peer not registered.

“Spawn path. Spawnable list. Authority on the parent. Three checks for replicated worlds.”

Related Issues

For multiplayer signaling failures, see Multiplayer Signaling Connection Failed. For autoload visibility issues, see Autoload Not Accessible.

Path matches. Scene listed. Authority set. The world appears for everyone.