Quick answer: Mirror crashes are confusing because the same NetworkBehaviour code runs on the server, on clients, and in host mode, and a bare stack trace hides which role failed. To fix them, capture whether the crash happened on the server, a client, or a host, plus the NetworkBehaviour, the netId, authority, and whether a Command or ClientRpc was in flight. Group by that signature and the side that breaks becomes obvious.

Mirror gives Unity developers a clean way to write networked behaviour, but its strength is also its trap: one NetworkBehaviour script runs on the server, on every client, and inside the host that is both at once. When it crashes, the stack trace looks identical regardless of which role hit it, so you cannot tell whether the server choked spawning an object, a client mishandled a SyncVar update, or the host tripped over a Command meant only for remote clients. This post covers the crashes Mirror games actually ship with and how to capture the role and connection context that turns an ambiguous exception into a fix.

The server, client, and host ambiguity

Mirror runs the same code in three contexts and gates behaviour with isServer, isClient, isLocalPlayer, and hasAuthority. Most crashes come from a check that is wrong or missing: a Command runs server side but touches a client only field, a ClientRpc fires on a client whose object was already destroyed, or host mode runs both paths and exposes an ordering you never see on a dedicated server. A stack trace cannot distinguish these because the code line is the same; only the role tells you the real story.

That is why the first piece of context to capture is the mode the build is running in: dedicated server, pure client, or host. The next is which guard the code thought it was under when it threw. A null reference inside an OnStartClient handler means something different on the server than on a client, and host mode crashes often reveal that a method assumed it ran on only one side. Without the role, you are debugging three different bugs that all look the same.

NetworkBehaviour and authority context

Beyond the role, the netId of the object that crashed, its NetworkBehaviour component, and whether the local connection had authority over it are the fields that make Mirror crashes reproducible. Authority bugs are especially common: a client tries to mutate an object it does not own, a Command is rejected, or a SyncVar hook fires on a stale object. Capturing hasAuthority and isOwned at crash time tells you instantly whether the failure is an ownership mistake or a genuine logic error.

Spawn and despawn timing is the other frequent culprit. Mirror spawns and destroys networked objects asynchronously, so a ClientRpc or SyncVar update can arrive for an object that no longer exists locally, or before it has finished initializing. Recording whether the object was spawned and active when the crash occurred, plus the connection id, lets you see whether you are looking at a lifecycle race. These are the bugs that vanish on a fast local network and reappear under real latency.

Commands, RPCs, and the message in flight

Mirror crashes frequently happen while a Command or RPC is being processed, so capturing which message was in flight is enormously clarifying. A Command that deserializes a parameter into an invalid game state, a ClientRpc that targets a destroyed object, or a TargetRpc sent to a disconnected connection all produce exceptions that look generic but trace back to a specific message. Record the message type and the direction it was traveling when the crash fired.

This matters because the fix differs by message direction. A crash during a Command means the server trusted client input it should have validated, which is both a stability and a security concern. A crash during an RPC means the server sent state a client could not handle, often because the client was mid disconnect or mid scene change. Knowing the message and its direction points you straight at which side of the contract to harden rather than sprinkling defensive null checks across every networked method.

Disconnects and scene transitions

A large share of Mirror crashes cluster around disconnects and scene changes, because both tear down networked objects while messages may still be in flight. A player disconnects mid match, the server destroys their objects, and a queued RPC or SyncVar update then references something gone. Or the host changes scenes and clients receive spawn messages for a scene they have not loaded. Capturing the connection state and current scene at crash time exposes these transition races immediately.

The practical defense is to treat every networked callback as something that can run during teardown. But you only know which callbacks need that hardening if your crash reports tell you they fired during a disconnect or scene load. Attaching the connection state, whether a scene transition was in progress, and the time since the last network message lets you separate steady state logic bugs from the teardown races that dominate real world Mirror crash reports.

Setting it up with Bugnet

Bugnet captures Mirror crashes with their full Unity stack trace and device context, and the in game report button snapshots game state so you also see the scene and objects involved. The key to making Mirror tractable is using custom fields to attach the role (server, client, or host), the crashing NetworkBehaviour and netId, the authority flags, and the Command or RPC in flight. Those fields turn three ambiguous bugs that share a stack trace into three distinct, filterable issues in one dashboard.

Bugnet groups duplicate reports into a single issue with an occurrence count, which is ideal for networked crashes that fire across many sessions and roles. You can see at a glance that a SyncVar hook crash hit 180 times, all on clients, all during a scene transition, while a near identical looking server crash is actually a separate, rarer authority bug. Filtering by role and message direction lets you confirm the pattern and prioritize the fix that protects the most players.

A workflow that respects all three roles

Bake role detection into your reporting layer once so every Mirror crash automatically records whether it came from the server, a client, or a host. It is a few lines reading isServer and isClient at report time, and it permanently removes the worst ambiguity from your crash data. After that, your discipline is simply to read crashes through the lens of which role failed and what message was in flight, rather than treating the stack trace as the whole truth.

Test all three roles deliberately before each release: run a dedicated server with separate clients, then run in host mode, since host exercises code paths neither pure role does. When grouped reports show a crash concentrated in one role, you know exactly which configuration to reproduce in. Over time your guards around authority, spawn timing, and disconnect handling tighten, and the steady drip of role confused crashes that plagues most Mirror games dries up.

In Mirror the same line crashes on the server, on clients, and in the host. Capture the role and the message in flight, or you are debugging three bugs as if they were one.