{"record":{"id":"122aee89dc2a724c","repo":"apache/cassandra","slug":"unexpected-message-startup-the-connection-is-alre","errorCode":null,"errorMessage":"Unexpected message STARTUP, the connection is already initialized","messagePattern":"Unexpected message STARTUP, the connection is already initialized","errorType":"exception","errorClass":"ProtocolException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/transport/ServerConnection.java","lineNumber":94,"sourceCode":"        return stage;\n    }\n\n    QueryState validateNewMessage(Message.Type type, ProtocolVersion version)\n    {\n        switch (stage)\n        {\n            case ESTABLISHED:\n                if (type != Message.Type.STARTUP && type != Message.Type.OPTIONS)\n                    throw new ProtocolException(String.format(\"Unexpected message %s, expecting STARTUP or OPTIONS\", type));\n                break;\n            case AUTHENTICATING:\n                // Support both SASL auth from protocol v2 and the older style Credentials auth from v1\n                if (type != Message.Type.AUTH_RESPONSE && type != Message.Type.CREDENTIALS)\n                    throw new ProtocolException(String.format(\"Unexpected message %s, expecting %s\", type, version == ProtocolVersion.V1 ? \"CREDENTIALS\" : \"SASL_RESPONSE\"));\n                break;\n            case READY:\n                if (type == Message.Type.STARTUP)\n                    throw new ProtocolException(\"Unexpected message STARTUP, the connection is already initialized\");\n                break;\n            default:\n                throw new AssertionError();\n        }\n\n        return new QueryState(clientState);\n    }\n\n    void applyStateTransition(Message.Type requestType, Message.Type responseType)\n    {\n        switch (stage)\n        {\n            case ESTABLISHED:\n                if (requestType == Message.Type.STARTUP)\n                {\n                    if (responseType == Message.Type.AUTHENTICATE)\n                        stage = ConnectionStage.AUTHENTICATING;\n                    else if (responseType == Message.Type.READY)","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/transport/ServerConnection.java#L76-L112","documentation":"A ProtocolException thrown when a client sends a second STARTUP frame on a connection that already completed initialization (stage READY). The CQL binary protocol allows STARTUP only once per connection; re-initialization requires a new connection. The server rejects the duplicate STARTUP while still allowing other messages in the READY state.","triggerScenarios":"Client sends STARTUP again after the connection is READY (already did STARTUP, optional auth, and is serving queries); validateNewMessage() in READY stage sees type == STARTUP and throws.","commonSituations":"Client reconnect logic reusing the same connection object and re-sending STARTUP, driver bug after a session re-init, connection wrappers that reset options by re-running the handshake, or hand-rolled clients toggling compression via a new STARTUP.","solutions":["Reuse the connection without resending STARTUP; create a brand-new connection if re-initialization is needed","Fix reconnect/retry logic to establish a fresh socket instead of replaying the handshake","Don't try to change compression/options mid-connection — set them in the initial STARTUP only","Check driver version for known bugs re-sending STARTUP on recovery"],"exampleFix":"// before (re-handshake on same connection after error)\nconnection.send(startup); // already READY\n// after\nconnection.close();\nConnection fresh = factory.newConnection();\nfresh.send(startup);","handlingStrategy":"type-guard","validationCode":"if (connection.isReady() && message instanceof StartupMessage)\n    throw new IllegalStateException(\"STARTUP is only allowed once per connection; open a new connection instead\");","typeGuard":"boolean maySendStartup(Connection c) { return c.state() == ConnectionState.FRESH || c.state() == ConnectionState.ESTABLISHED; }","tryCatchPattern":"try { connection.send(startup); } catch (ProtocolException e) { if (e.getMessage().contains(\"already initialized\")) { connection.close(); connection = openNewConnection(); } else throw e; }","preventionTips":["Send STARTUP exactly once per connection lifetime","On reconnect, create a new connection object rather than re-handshaking","Set compression/options only in the initial STARTUP","Review retry/recovery code for accidental handshake replay"],"tags":["cql-protocol","state-machine","duplicate-startup"],"backgroundTag":"invalid-state-transition","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}