Quick answer: NetworkObject.ChangeOwnership only works when called on the server. Clients cannot change ownership directly. Implement a ServerRpc that the client calls to request ownership, then have the server call ChangeOwnership(requestingClientId). Verify with IsOwner on the client side after the transfer completes.

Here is how to fix Unity Netcode for GameObjects ownership not transferring. A player picks up an item and your code calls ChangeOwnership to give them authority over it — but nothing happens. The object stays server-owned, the client cannot move it, and no error appears in the log. The ownership transfer is silently ignored because only the server has permission to change ownership, and your call is happening on the client.

The Symptom

You call NetworkObject.ChangeOwnership(clientId) and the OwnerClientId does not change. IsOwner remains false on the intended new owner. The object continues to be server-controlled. No exceptions, no error messages — the transfer simply does not happen. It may work when running as host but fail with a dedicated server and separate clients.

What Causes This

ChangeOwnership called on a client. NetworkObject.ChangeOwnership is a server-only API. When called on a client, it silently does nothing. There is no client-side ownership API because ownership is an authoritative server concept — clients cannot self-assign authority.

Using a ClientRpc instead of ServerRpc. If you invoke the ownership change in a ClientRpc (which runs on clients), you are calling a server-only method on clients. The flow must be: client sends ServerRpc → server validates → server calls ChangeOwnership.

Wrong clientId or unspawned object. Passing an invalid clientId (a disconnected client) causes silent failure. Similarly, ChangeOwnership only works on spawned NetworkObjects — unspawned objects ignore the call.

RequireOwnership on ServerRpc. [ServerRpc(RequireOwnership = true)] (the default) means only the current owner can call the RPC. If the server owns the object, clients cannot invoke it. Set RequireOwnership = false for request-style RPCs.

The Fix

Step 1: Implement a ServerRpc request pattern.

using Unity.Netcode;

public class PickupItem : NetworkBehaviour
{
    [ServerRpc(RequireOwnership = false)]
    public void RequestOwnershipServerRpc(ServerRpcParams rpcParams = default)
    {
        ulong requestingClient = rpcParams.Receive.SenderClientId;

        // Validate: is this client allowed to take ownership?
        if (!CanClientTakeOwnership(requestingClient))
            return;

        // Transfer ownership on the server
        NetworkObject.ChangeOwnership(requestingClient);
    }

    private bool CanClientTakeOwnership(ulong clientId)
    {
        // Add game logic: is item already held? Is client in range?
        return NetworkObject.OwnerClientId == NetworkManager.ServerClientId;
    }
}

Step 2: Call the ServerRpc from the client.

// Client-side pickup interaction
public class PlayerInteraction : NetworkBehaviour
{
    void Update()
    {
        if (!IsOwner) return; // Only local player processes input

        if (Input.GetKeyDown(KeyCode.E))
        {
            if (TryGetNearbyItem(out PickupItem item))
            {
                item.RequestOwnershipServerRpc();
            }
        }
    }
}

Step 3: React to ownership changes. Override OnGainedOwnership and OnLostOwnership to respond when the transfer completes.

public override void OnGainedOwnership()
{
    Debug.Log("I now own this object!");
    // Enable client-authoritative movement, show UI, etc.
}

public override void OnLostOwnership()
{
    Debug.Log("Ownership lost — disabling input");
}

Step 4: Handle RequireOwnership correctly. For ServerRpc methods that any client should be able to call (like requesting ownership of an unowned item), always set RequireOwnership = false. The default true blocks non-owners from calling the RPC.

“Ownership in Netcode is a privilege granted by the server, not a right claimed by the client. Always go through a ServerRpc.”

Debugging Ownership

Enable verbose logging with NetworkManager.Singleton.LogLevel = LogLevel.Developer to see ownership changes in the console. Use the OnGainedOwnership/OnLostOwnership callbacks to trace the transfer flow.

If ChangeOwnership does nothing, you are on a client. Move that call to a ServerRpc. That is the fix in 95% of cases.