apache/cassandra · error · InvalidRequestException

May only delete on txn_id and id_micros

Error message

May only delete on txn_id and id_micros

What it means

In the AccordDebugKeyspace txn_id-keyed virtual table, range tombstones over the clustering columns are only meaningful when the range is expressed on the single clustering column id_micros. applyRangeTombstone throws this error when the DELETE's clustering range involves more than one clustering column bound (starts.length > 1 or ends.length > 1), since the table cannot map multi-column ranges onto a single id_micros interval.

Solutions

  1. Restrict the DELETE range to only the id_micros clustering column: `DELETE FROM ... WHERE txn_id = '...' AND id_micros >= x AND id_micros <= y`.
  2. Delete individual rows one at a time instead of a multi-column range.
  3. Delete the entire partition (`DELETE FROM ... WHERE txn_id = '...';`) if the table supports it.
  4. If broader cleanup is needed, use the underlying Accord state tooling rather than this debug table.

Example fix

// before
DELETE FROM system.accord_debug WHERE txn_id = '...' AND id_micros > 5 AND some_col < 10; // multi-column range
// after
DELETE FROM system.accord_debug WHERE txn_id = '...' AND id_micros > 5; // single-column range only
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the DELETE range touches only the id_micros clustering column
if (whereClauseColumns.stream().filter(c -> !c.equals("txn_id") && !c.equals("id_micros")).count() > 0)
    throw new IllegalArgumentException("Only txn_id and id_micros predicates allowed");

Try / catch

try { session.execute(deleteStmt); }
catch (InvalidRequestException e) { if (e.getMessage().equals("May only delete on txn_id and id_micros")) { /* rewrite range on id_micros only */ } else throw e; }

Prevention

When it happens

Trigger: Executing a ranged DELETE like `DELETE FROM table WHERE txn_id = '...' AND id_micros > 1 AND other_col < 5` (or any multi-clustering-column range) against this debug virtual table.

Common situations: Administrators cleaning up Accord txn records with generic range-delete queries modeled after normal tables with composite clustering keys; debugging scripts assuming full CQL range semantics on virtual tables.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/java/org/apache/cassandra/db/virtual/AccordDebugKeyspace.java:1240

                        "  command_store_id int,\n" +
                        "  remote_node_id int,\n" +
                        "  message text,\n" +
                        "  PRIMARY KEY (txn_id, id_micros, event, at_micros)" +
                        ')', TxnIdUtf8Type.instance), FAIL, UNSORTED, UNSORTED);
        }

        @Override
        protected void applyPartitionDeletion(Object[] partitionKeys)
        {
            TxnId txnId = TxnId.parse((String)partitionKeys[0]);
            tracing().eraseEvents(txnId);
        }

        @Override
        protected void applyRangeTombstone(Object[] partitionKeys, Object[] starts, boolean startInclusive, Object[] ends, boolean endInclusive)
        {
            TxnId txnId = TxnId.parse((String) partitionKeys[0]);
            if (starts.length > 1 || ends.length > 1) throw invalidRequest("May only delete on txn_id and id_micros");

            long minId = Long.MIN_VALUE, maxId = Long.MAX_VALUE;
            if (starts.length == 1)
            {
                minId = ((Long)starts[0]);
                if (!startInclusive && minId < Long.MAX_VALUE)
                    ++minId;
            }
            if (ends.length == 1)
            {
                maxId = ((Long)ends[0]);
                if (!endInclusive && maxId > Long.MIN_VALUE)
                    --maxId;
            }
            tracing().eraseEventsBetween(txnId, minId, maxId);
        }

        @Override

View on GitHub (pinned to 88fd0f6a0e)