Anuken/Mindustry · error · IOException
Failed to send packet: {}
Error message
Failed to send packet: {} What it means
Thrown inside sendClient after snet.sendMessageToConnection returns SteamResult.InvalidParam, NoConnection, or InvalidState. These three codes mean the connection is unusable (dropped, not yet established, or given bad arguments), so the packet cannot be delivered. The exception is then caught by the surrounding try and passed to net.showError, surfacing a network-failure dialog to the player.
Source
Thrown at desktop/src/mindustry/desktop/steam/SNet.java:223
@Override
public void sendClient(Object object, boolean reliable){
if(isSteamClient()){
if(currentServer == null || clientConnection == null){
Log.info("Not connected, quitting.");
return;
}
try{
clientWriteBuffer.limit(clientWriteBuffer.capacity());
clientWriteBuffer.position(0);
serializer.write(clientWriteBuffer, object);
int length = clientWriteBuffer.position();
clientWriteBuffer.flip();
var result = snet.sendMessageToConnection(clientConnection, clientWriteBuffer, reliable || length >= 1000 ? SendFlags.ReliableNoNagle : SendFlags.UnreliableNoDelay);
if(result == SteamResult.InvalidParam || result == SteamResult.NoConnection || result == SteamResult.InvalidState){
throw new IOException("Failed to send packet: " + result);
}
}catch(Exception e){
net.showError(e);
}
}else{
provider.sendClient(object, reliable);
}
}
@Override
public void disconnectClient(){
stopNetThread();
if(isSteamClient()){
if(currentLobby != null) smat.leaveLobby(currentLobby);
if(clientConnection != null) snet.closeConnection(clientConnection, 0, false);
clientConnection = null;
currentServer = null;View on GitHub (pinned to f695ad7e60)
Solutions
- Gate sends on a connected flag that is set only when onConnectionStatusChanged reports Connected and cleared on None/FailedByRemote.
- Treat NoConnection as recoverable: suppress the error dialog and trigger a reconnect instead of showing it.
- Drop or re-queue early-sends during the connecting window rather than letting them hit sendMessageToConnection.
- Log the SteamResult value so transient vs. fatal failures are distinguishable.
Example fix
// before
var result = snet.sendMessageToConnection(clientConnection, clientWriteBuffer, reliable || length >= 1000 ? SendFlags.ReliableNoNagle : SendFlags.UnreliableNoDelay);
if(result == SteamResult.InvalidParam || result == SteamResult.NoConnection || result == SteamResult.InvalidState){
throw new IOException("Failed to send packet: " + result);
}
// after
var result = snet.sendMessageToConnection(clientConnection, clientWriteBuffer, reliable || length >= 1000 ? SendFlags.ReliableNoNagle : SendFlags.UnreliableNoDelay);
if(result == SteamResult.NoConnection || result == SteamResult.InvalidState){
Log.warn("Send failed, connection lost (@); suppressing.", result);
netClient.disconnectQuietly();
return;
}
if(result == SteamResult.InvalidParam){
throw new IOException("Failed to send packet: " + result);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Track connection readiness; only send when Connected
if(!isSteamClient() || clientConnection == null || !connectionReady){
Log.info("Not connected, skipping send.");
return;
} Type guard
static boolean canSendToSteam(Connection c, boolean ready){
return c != null && ready;
} Try / catch
try{
serializer.write(clientWriteBuffer, object);
var result = snet.sendMessageToConnection(clientConnection, clientWriteBuffer, flags);
if(result == SteamResult.NoConnection || result == SteamResult.InvalidState){
Log.warn("P2P send failed (@); reconnecting.", result);
netClient.disconnectQuietly();
return;
}
if(result == SteamResult.InvalidParam) throw new IOException("Failed to send packet: " + result);
}catch(Exception e){
net.showError(e);
} Prevention
- Set a connectionReady flag only when onConnectionStatusChanged reports Connected and clear it on any terminal state.
- Queue early sends instead of flushing them during the connecting window.
- Treat NoConnection as recoverable (reconnect) rather than fatal to avoid noisy error dialogs.
- Always check currentServer/clientConnection for null before sending, as the existing guard already does.
When it happens
Trigger: Calling sendClient while clientConnection is closed/timed out (NoConnection), before connectP2P has reached the Connected state (InvalidState), or with a buffer/connection in an inconsistent state (InvalidParam). Common during the race between connection setup and queued sends, or after a silent disconnect.
Common situations: Sending packets immediately after connectP2P before onConnectionStatusChanged fires; sending after the peer dropped without local detection; a large reliable packet hitting a connection that the peer already closed; mobile network handoff severing the P2P link.
Related errors
- Invalid Steam ID: {}
- Invalid Steam ID structure: {}
- Failed to parse server Steam ID: {}
- A mod with the name '{baseName}' is already imported.
- alreadyconnected
AI-assisted analysis of Anuken/Mindustry@f695ad7e60 (2026-08-14).
Data as JSON: /api/errors/31c232d7b721ebb0.
Report an issue: GitHub.