{"record":{"id":"4d6b4f8e163baef7","repo":"apache/cassandra","slug":"cannot-provide-custom-timestamp-for-counter-batch","errorCode":null,"errorMessage":"Cannot provide custom timestamp for counter BATCH","messagePattern":"Cannot provide custom timestamp for counter BATCH","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/BatchStatement.java","lineNumber":265,"sourceCode":"        ModificationStatement.TableAuthorizationState authorizationState = new ModificationStatement.TableAuthorizationState();\n        for (ModificationStatement statement : statements)\n            statement.authorize(state, authorizationState);\n    }\n\n    // Validates a prepared batch statement without validating its nested statements.\n    public void validate() throws InvalidRequestException\n    {\n        if (attrs.isTimeToLiveSet())\n            throw new InvalidRequestException(\"Global TTL on the BATCH statement is not supported.\");\n\n        boolean timestampSet = attrs.isTimestampSet();\n        if (timestampSet)\n        {\n            if (hasConditions)\n                throw new InvalidRequestException(\"Cannot provide custom timestamp for conditional BATCH\");\n\n            if (isCounter())\n                throw new InvalidRequestException(\"Cannot provide custom timestamp for counter BATCH\");\n        }\n\n        boolean hasCounters = false;\n        boolean hasNonCounters = false;\n\n        boolean hasVirtualTables = false;\n        boolean hasRegularTables = false;\n\n        for (ModificationStatement statement : statements)\n        {\n            if (timestampSet && statement.isTimestampSet())\n                throw new InvalidRequestException(\"Timestamp must be set either on BATCH or individual statements: \" + statement.source);\n\n            if (statement.isCounter())\n                hasCounters = true;\n            else\n                hasNonCounters = true;\n","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java#L247-L283","documentation":"Counter mutations have no user-settable writetime (they use server-side counters), so a counter batch may not include USING TIMESTAMP. BatchStatement.validate() throws this InvalidRequestException when timestampSet && isCounter().","triggerScenarios":"BEGIN BATCH ... USING TIMESTAMP n ... where all statements target counter tables (isCounter() true), executed directly or via prepare.","commonSituations":"Reusing a generic write helper that always sets timestamps on batches; migrating regular-column writes to counters without stripping USING TIMESTAMP.","solutions":["Remove USING TIMESTAMP from the counter batch","If timestamp control is required, use regular (non-counter) tables instead of counters"],"exampleFix":"// before\nBEGIN BATCH USING TIMESTAMP 1234 UPDATE counters SET c = c + 1 WHERE k = 'a'; APPLY BATCH;\n// after\nBEGIN BATCH UPDATE counters SET c = c + 1 WHERE k = 'a'; APPLY BATCH;","handlingStrategy":"validation","validationCode":"boolean isCounterBatch = statements.stream().allMatch(BatchStatement::isCounter);\nif (isCounterBatch && batchUsesTimestamp) throw new IllegalArgumentException(\"USING TIMESTAMP not allowed on counter batches\");","typeGuard":null,"tryCatchPattern":"try { session.execute(batch); }\ncatch (InvalidQueryException e) {\n    if (e.getMessage().contains(\"custom timestamp for counter BATCH\")) { /* strip USING TIMESTAMP */ }\n}","preventionTips":["Counters do not support client timestamps — never generate USING TIMESTAMP on counter writes","Filter by table type before applying global batch options"],"tags":["cql","batch","counters"],"backgroundTag":"conflicting-config-options","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}