{"record":{"id":"8d49d94e7bc44487","repo":"apache/cassandra","slug":"missing-value-cql-version-in-startup-message","errorCode":null,"errorMessage":"Missing value CQL_VERSION in STARTUP message","messagePattern":"Missing value CQL_VERSION in STARTUP message","errorType":"exception","errorClass":"ProtocolException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/transport/messages/StartupMessage.java","lineNumber":88,"sourceCode":"        }\n    };\n\n    private static final byte[] EMPTY_CLIENT_RESPONSE = new byte[0];\n\n    public final Map<String, String> options;\n\n    public StartupMessage(Map<String, String> options)\n    {\n        super(Message.Type.STARTUP);\n        this.options = options;\n    }\n\n    @Override\n    protected Message.Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)\n    {\n        String cqlVersion = options.get(CQL_VERSION);\n        if (cqlVersion == null)\n            throw new ProtocolException(\"Missing value CQL_VERSION in STARTUP message\");\n\n        try\n        {\n            if (new CassandraVersion(cqlVersion).compareTo(new CassandraVersion(\"2.99.0\")) < 0)\n                throw new ProtocolException(String.format(\"CQL version %s is not supported by the binary protocol (supported version are >= 3.0.0)\", cqlVersion));\n        }\n        catch (IllegalArgumentException e)\n        {\n            throw new ProtocolException(e.getMessage());\n        }\n\n        if (options.containsKey(COMPRESSION))\n        {\n            String compression = toLowerCaseLocalized(options.get(COMPRESSION));\n            if (compression.equals(\"snappy\"))\n            {\n                if (Compressor.SnappyCompressor.instance == null)\n                    throw new ProtocolException(\"This instance does not support Snappy compression\");","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/transport/messages/StartupMessage.java#L70-L106","documentation":"The STARTUP message must include a CQL_VERSION option declaring which CQL specification the client speaks. If the option is absent, StartupMessage throws a ProtocolException, because the server cannot interpret the session without knowing the CQL version.","triggerScenarios":"Sending a StartupMessage whose options map lacks the \"CQL_VERSION\" key — e.g. hand-built protocol frames, minimal proxies/drivers omitting startup options, or a config layer that strips empty option values.","commonSituations":"Custom/native-protocol clients built from incomplete examples; thin clients forwarding only COMPRESSION; test harnesses constructing bare STARTUP frames; drivers with broken option serialization.","solutions":["Always include CQL_VERSION (e.g. \"3.0.0\") in the STARTUP options map","Check any code that builds/sanitizes the options map so it doesn't drop the version entry","Use a maintained driver that sends STARTUP correctly rather than hand-rolled frames","Verify with a protocol trace (netty logging / wireshark) that the option is actually on the wire"],"exampleFix":"// before\nMap<String,String> opts = Map.of(\"COMPRESSION\",\"lz4\");\n// after\nMap<String,String> opts = Map.of(\"CQL_VERSION\",\"3.0.0\",\"COMPRESSION\",\"lz4\");","handlingStrategy":"validation","validationCode":"if (!options.containsKey(\"CQL_VERSION\"))\n    throw new IllegalArgumentException(\"STARTUP requires CQL_VERSION\");","typeGuard":"boolean startupOptionsValid(Map<String,String> o) {\n    return o != null && o.containsKey(\"CQL_VERSION\");\n}","tryCatchPattern":"try {\n    connection.startup(options);\n} catch (ProtocolException e) {\n    if (e.getMessage().contains(\"Missing value CQL_VERSION\")) {\n        options.put(\"CQL_VERSION\", \"3.0.0\");\n        connection.startup(options);\n    } else throw e;\n}","preventionTips":["Always send CQL_VERSION in STARTUP options","Sanitize option maps carefully so required keys aren't stripped","Use maintained drivers rather than hand-built frames","Assert startup options in protocol-level integration tests"],"tags":["cql","startup","protocol","missing-option"],"backgroundTag":"missing-required-argument","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"}