apache/cassandra · error · InvalidRequestException

Cannot provide custom timestamp for conditional BATCH

Error message

Cannot provide custom timestamp for conditional BATCH

What it means

A batch that contains conditional statements (LWT, e.g. IF NOT EXISTS / IF condition) cannot also carry a batch-level custom timestamp USING TIMESTAMP, because condition evaluation determines the write timestamp. BatchStatement.validate() throws this InvalidRequestException.

Source

Thrown at src/java/org/apache/cassandra/cql3/statements/BatchStatement.java:262

    @Override
    public void authorize(ClientState state) throws InvalidRequestException, UnauthorizedException
    {
        ModificationStatement.TableAuthorizationState authorizationState = new ModificationStatement.TableAuthorizationState();
        for (ModificationStatement statement : statements)
            statement.authorize(state, authorizationState);
    }

    // Validates a prepared batch statement without validating its nested statements.
    public void validate() throws InvalidRequestException
    {
        if (attrs.isTimeToLiveSet())
            throw new InvalidRequestException("Global TTL on the BATCH statement is not supported.");

        boolean timestampSet = attrs.isTimestampSet();
        if (timestampSet)
        {
            if (hasConditions)
                throw new InvalidRequestException("Cannot provide custom timestamp for conditional BATCH");

            if (isCounter())
                throw new InvalidRequestException("Cannot provide custom timestamp for counter BATCH");
        }

        boolean hasCounters = false;
        boolean hasNonCounters = false;

        boolean hasVirtualTables = false;
        boolean hasRegularTables = false;

        for (ModificationStatement statement : statements)
        {
            if (timestampSet && statement.isTimestampSet())
                throw new InvalidRequestException("Timestamp must be set either on BATCH or individual statements: " + statement.source);

            if (statement.isCounter())
                hasCounters = true;

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Remove USING TIMESTAMP from the conditional batch
  2. Move the conditional statements to a separate batch without a custom timestamp
  3. Use per-statement timestamps only on non-conditional statements in a different batch

Example fix

// before
BEGIN BATCH USING TIMESTAMP 1234 INSERT INTO t ... IF NOT EXISTS; APPLY BATCH;
// after
BEGIN BATCH INSERT INTO t ... IF NOT EXISTS; APPLY BATCH; // no USING TIMESTAMP
Defensive patterns

Strategy: validation

Validate before calling

boolean hasConditions = statements.stream().anyMatch(s -> s.hasConditions());
if (hasConditions && batchUsesTimestamp) throw new IllegalArgumentException("drop USING TIMESTAMP for conditional batches");

Try / catch

try { session.execute(batch); }
catch (InvalidQueryException e) {
    if (e.getMessage().contains("custom timestamp for conditional BATCH")) { /* remove USING TIMESTAMP */ }
}

Prevention

When it happens

Trigger: BEGIN BATCH ... USING TIMESTAMP 12345 ... containing any statement with conditions (hasConditions), executed directly or via prepare.

Common situations: Combining compare-and-set workflows with explicit timestamps; query builders that always append USING TIMESTAMP.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/63ceb09a272b9f2a. Report an issue: GitHub.