{"record":{"id":"3b5c8be738897849","repo":"apache/cassandra","slug":"invalid-query-must-be-a-s-statement-but-was-s","errorCode":null,"errorMessage":"Invalid query, must be a %s statement but was: %s","messagePattern":"Invalid query, must be a (.+?) statement but was: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java","lineNumber":717,"sourceCode":"            if (insert.hasConditions())\n                throw new IllegalArgumentException(\"Conditional statements are not supported\");\n            if (insert.isCounter())\n                throw new IllegalArgumentException(\"Counter update statements are not supported\");\n            if (insert.getBindVariables().isEmpty())\n                throw new IllegalArgumentException(\"Provided insert statement has no bind variables\");\n\n            return insert;\n        }\n    }\n\n    public static <T extends CQLStatement.Raw> T parseStatement(String query, Class<T> klass, String type)\n    {\n        try\n        {\n            CQLStatement.Raw stmt = CQLFragmentParser.parseAnyUnhandled(CqlParser::query, query);\n\n            if (!stmt.getClass().equals(klass))\n                throw new IllegalArgumentException(\"Invalid query, must be a \" + type + \" statement but was: \" + stmt.getClass());\n\n            return klass.cast(stmt);\n        }\n        catch (RecognitionException | RequestValidationException e)\n        {\n            throw new IllegalArgumentException(e.getMessage(), e);\n        }\n    }\n}\n","sourceCodeStart":699,"sourceCodeEnd":727,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java#L699-L727","documentation":"StressCQLSSTableWriter.parseStatement parses a CQL string with the ANTLR parser and then verifies that the resulting statement object is of the expected class (e.g. an INSERT for a writer opened as an insert writer, or DELETE/UPDATE for the corresponding variant). If the parsed statement is a different kind of statement, it throws IllegalArgumentException telling the caller what kind of statement was expected and what class was actually parsed. This guards the SSTable-writer API, which can only generate SSTables for the statement type it was constructed for.","triggerScenarios":"Calling StressCQLSSTableWriter.builder(...).build() for one operation kind (e.g. INSERT) and then passing a CQL string of a different kind (SELECT, UPDATE, DELETE) to the writer, so CQLFragmentParser.parseAnyUnhandled(CqlParser::query, query) returns a Raw statement whose class does not equal the expected klass.","commonSituations":"Copy-pasting a CQL statement of the wrong type into the writer builder; changing a schema string from INSERT to UPDATE (or adding options like IF NOT EXISTS) after configuring the writer; confusing compileOptions/insert vs update/delete writer factory methods in the stress tooling.","solutions":["Make the CQL string passed to the writer match the writer kind: use an INSERT statement for an insert writer.","Check that the statement does not start with a different keyword (SELECT/UPDATE/DELETE/BEGIN) and remove trailing semicolons or comments that could confuse classification.","If you need a different statement type, build the writer with the matching factory (StressCQLSSTableWriter.builder for inserts vs the delete/update variants).","Wrap the call in try/catch (IllegalArgumentException) and print the actual parsed statement class from the message to diagnose the mismatch."],"exampleFix":"// before\nStressCQLSSTableWriter writer = StressCQLSSTableWriter.builder(schema, \"UPDATE ks.t SET v = ? WHERE k = ?\").build();\n// after\nStressCQLSSTableWriter writer = StressCQLSSTableWriter.builder(schema, \"INSERT INTO ks.t (k, v) VALUES (?, ?)\").build();","handlingStrategy":"validation","validationCode":"String cql = \"INSERT INTO ks.t (k, v) VALUES (?, ?)\";\nString firstWord = cql.trim().split(\"\\\\s+\", 2)[0].toUpperCase();\nif (!firstWord.equals(\"INSERT\"))\n    throw new IllegalArgumentException(\"Writer expects INSERT, got: \" + firstWord);","typeGuard":"boolean isInsert(Class<? extends CQLStatement.Raw> actual, Class<? extends CQLStatement.Raw> expected) {\n    return expected.equals(actual);\n}","tryCatchPattern":"try {\n    writer = StressCQLSSTableWriter.builder(schema, cql).build();\n} catch (IllegalArgumentException e) {\n    throw new IllegalStateException(\"CQL does not match writer kind: \" + e.getMessage(), e);\n}","preventionTips":["Always generate the CQL string from the same constant used to select the writer kind.","Strip trailing semicolons and comments before passing CQL to the builder.","Unit-test writer construction for every profile statement type.","Keep one factory method per statement type and never pass raw strings across kinds."],"tags":["cql","sstable-writer","argument-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}