{"record":{"id":"31c232d7b721ebb0","repo":"Anuken/Mindustry","slug":"failed-to-send-packet","errorCode":null,"errorMessage":"Failed to send packet: {}","messagePattern":"Failed to send packet: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"desktop/src/mindustry/desktop/steam/SNet.java","lineNumber":223,"sourceCode":"    @Override\n    public void sendClient(Object object, boolean reliable){\n        if(isSteamClient()){\n            if(currentServer == null || clientConnection == null){\n                Log.info(\"Not connected, quitting.\");\n                return;\n            }\n\n            try{\n                clientWriteBuffer.limit(clientWriteBuffer.capacity());\n                clientWriteBuffer.position(0);\n                serializer.write(clientWriteBuffer, object);\n                int length = clientWriteBuffer.position();\n                clientWriteBuffer.flip();\n\n                var result = snet.sendMessageToConnection(clientConnection, clientWriteBuffer, reliable || length >= 1000 ? SendFlags.ReliableNoNagle : SendFlags.UnreliableNoDelay);\n\n                if(result == SteamResult.InvalidParam || result == SteamResult.NoConnection || result == SteamResult.InvalidState){\n                    throw new IOException(\"Failed to send packet: \" + result);\n                }\n            }catch(Exception e){\n                net.showError(e);\n            }\n        }else{\n            provider.sendClient(object, reliable);\n        }\n    }\n\n    @Override\n    public void disconnectClient(){\n        stopNetThread();\n\n        if(isSteamClient()){\n            if(currentLobby != null) smat.leaveLobby(currentLobby);\n            if(clientConnection != null) snet.closeConnection(clientConnection, 0, false);\n            clientConnection = null;\n            currentServer = null;","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/Anuken/Mindustry/blob/f695ad7e60323ebced984fa26d0bcf0bc54296b4/desktop/src/mindustry/desktop/steam/SNet.java#L205-L241","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar result = snet.sendMessageToConnection(clientConnection, clientWriteBuffer, reliable || length >= 1000 ? SendFlags.ReliableNoNagle : SendFlags.UnreliableNoDelay);\nif(result == SteamResult.InvalidParam || result == SteamResult.NoConnection || result == SteamResult.InvalidState){\n    throw new IOException(\"Failed to send packet: \" + result);\n}\n\n// after\nvar result = snet.sendMessageToConnection(clientConnection, clientWriteBuffer, reliable || length >= 1000 ? SendFlags.ReliableNoNagle : SendFlags.UnreliableNoDelay);\nif(result == SteamResult.NoConnection || result == SteamResult.InvalidState){\n    Log.warn(\"Send failed, connection lost (@); suppressing.\", result);\n    netClient.disconnectQuietly();\n    return;\n}\nif(result == SteamResult.InvalidParam){\n    throw new IOException(\"Failed to send packet: \" + result);\n}","handlingStrategy":"try-catch","validationCode":"// Track connection readiness; only send when Connected\nif(!isSteamClient() || clientConnection == null || !connectionReady){\n    Log.info(\"Not connected, skipping send.\");\n    return;\n}","typeGuard":"static boolean canSendToSteam(Connection c, boolean ready){\n    return c != null && ready;\n}","tryCatchPattern":"try{\n    serializer.write(clientWriteBuffer, object);\n    var result = snet.sendMessageToConnection(clientConnection, clientWriteBuffer, flags);\n    if(result == SteamResult.NoConnection || result == SteamResult.InvalidState){\n        Log.warn(\"P2P send failed (@); reconnecting.\", result);\n        netClient.disconnectQuietly();\n        return;\n    }\n    if(result == SteamResult.InvalidParam) throw new IOException(\"Failed to send packet: \" + result);\n}catch(Exception e){\n    net.showError(e);\n}","preventionTips":["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."],"tags":["networking","steam","runtime","p2p"],"backgroundTag":null,"analyzedSha":"f695ad7e60323ebced984fa26d0bcf0bc54296b4","analyzedAt":"2026-08-14T04:31:16.262Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}