apache/cassandra · error · IllegalArgumentException

Counter update statements are not supported

Error message

Counter update statements are not supported

What it means

Counter writes cannot be materialized as plain offline SSTables by StressCQLSSTableWriter, since counters require special storage handling in Cassandra. prepareInsert() throws IllegalArgumentException when the parsed UpdateStatement reports isCounter().

Solutions

  1. Do not use StressCQLSSTableWriter for counter tables; write via a live cluster connection instead.
  2. Remove counter columns from the SSTable-generation target or use a regular column type.
  3. Load counter increments through normal client writes (e.g. CQL driver) or cassandra-stress.
  4. If exact counter state is needed offline, consult supported import paths for counter tables in your Cassandra version.

Example fix

// before
builder.using("UPDATE ks.counters SET c = c + ? WHERE k = ?");
// after
builder.using("INSERT INTO ks.events (k, v) VALUES (?, ?)"); // non-counter table
Defensive patterns

Strategy: validation

Validate before calling

// ensure target table has no counter columns before building the writer
if (tableHasCounterColumns(schema)) throw new IllegalArgumentException("counter tables unsupported by StressCQLSSTableWriter");

Try / catch

try { writer = builder.build(); }
catch (IllegalArgumentException e) { throw new IllegalStateException("use live-cluster writes for counters: " + e.getMessage(), e); }

Prevention

When it happens

Trigger: Passing an INSERT/UPDATE targeting a counter column (e.g. UPDATE tbl SET c = c + ? or INSERT with counter column) to using(...).

Common situations: Bulk-loading counter tables with the stress SSTable writer; schema migrated to counter type after the tooling was built; copy-pasting counter update CQL into the generator.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java:702

            return TableId.fromUUID(UUID.nameUUIDFromBytes(ArrayUtils.addAll(keyspace.getBytes(), table.getBytes())));
        }

        /**
         * Prepares insert statement for writing data to SSTable
         *
         * @return prepared Insert statement and it's bound names
         */
        private UpdateStatement prepareInsert()
        {
            ClientState state = ClientState.forInternalCalls();
            CQLStatement cqlStatement = insertStatement.prepare(state);
            UpdateStatement insert = (UpdateStatement) cqlStatement;
            insert.validate(state);

            if (insert.hasConditions())
                throw new IllegalArgumentException("Conditional statements are not supported");
            if (insert.isCounter())
                throw new IllegalArgumentException("Counter update statements are not supported");
            if (insert.getBindVariables().isEmpty())
                throw new IllegalArgumentException("Provided insert statement has no bind variables");

            return insert;
        }
    }

    public static <T extends CQLStatement.Raw> T parseStatement(String query, Class<T> klass, String type)
    {
        try
        {
            CQLStatement.Raw stmt = CQLFragmentParser.parseAnyUnhandled(CqlParser::query, query);

            if (!stmt.getClass().equals(klass))
                throw new IllegalArgumentException("Invalid query, must be a " + type + " statement but was: " + stmt.getClass());

            return klass.cast(stmt);
        }

View on GitHub (pinned to 88fd0f6a0e)