OpenRA/OpenRA · critical · InvalidDataException

Received packet from disconnected client '{clientId}'

Error message

Received packet from disconnected client '{clientId}'

What it means

Thrown by OrderManager.ReceiveOrders when an order packet arrives from a clientId that has no entry in the pendingOrders dictionary. Each connected client is registered in pendingOrders; a packet from an unknown id means the client was never registered (or was already removed as disconnected), so the packet cannot be queued and is treated as a protocol error.

Source

Thrown at OpenRA.Game/Network/OrderManager.cs:194

		public void ReceiveImmediateOrders(int clientId, OrderPacket orders)
		{
			foreach (var o in orders.GetOrders(World))
			{
				UnitOrders.ProcessOrder(this, World, clientId, o);

				// A mod switch or other event has pulled the ground from beneath us
				if (disposed)
					return;
			}
		}

		public void ReceiveOrders(int clientId, (int Frame, OrderPacket Orders) orders)
		{
			if (pendingOrders.TryGetValue(clientId, out var queue))
				queue.Enqueue((orders.Frame, orders.Orders));
			else
				throw new InvalidDataException($"Received packet from disconnected client '{clientId}'");
		}

		void ReceiveAllOrdersAndCheckSync()
		{
			Connection.Receive(this);
		}

		bool IsReadyForNextFrame => GameStarted && pendingOrders.All(p => p.Value.Count > 0);

		public int SuggestedTimestep
		{
			get
			{
				if (World == null)
					return Ui.Timestep;

				if (World.IsLoadingGameSave)
					return 1;

View on GitHub (pinned to a520984d91)

Solutions

  1. If you maintain custom networking, ensure packets for a clientId are only dispatched while that client is registered in pendingOrders (guard with a ContainsKey check before calling ReceiveOrders).
  2. For end users, this usually indicates a transient desync/disconnect; restarting the match typically resolves it.
  3. If persistent across all games, verify all peers run the same engine version.

Example fix

// caller guard before dispatching a packet to OrderManager
// before
orderManager.ReceiveOrders(o.ClientId, orders);
// after
if (pendingClientIds.Contains(o.ClientId))
    orderManager.ReceiveOrders(o.ClientId, orders);
Defensive patterns

Strategy: validation

Validate before calling

// Guard dispatch: only forward packets for registered clients
if (orderManager.PendingClientIds.Contains(o.ClientId))
    orderManager.ReceiveOrders(o.ClientId, orders);
else
    Log.Write("network", $"Dropping packet for unregistered client {o.ClientId}");

Try / catch

try { orderManager.ReceiveOrders(clientId, orders); }
catch (InvalidDataException ex) when (ex.Message.Contains("disconnected client"))
{ /* tolerate stray late packets from a just-disconnected client */ }

Prevention

When it happens

Trigger: ReceiveOrders(clientId, orders) is called where pendingOrders does not contain clientId — e.g. the client disconnected and was removed from pendingOrders, but a stray/late packet still arrives; or a server relayed a packet for an id the local client never saw register.

Common situations: A client disconnects and a final in-flight packet is processed after removal; network/server logic forwarding packets for a clientId outside the expected range; a reconnect or mod-switch state transition leaving pendingOrders inconsistent with the connection.

Related errors


AI-assisted analysis of OpenRA/OpenRA@a520984d91 (2026-08-13). Data as JSON: /api/errors/b67f0182a865a98e. Report an issue: GitHub.