apache/cassandra · error · InvalidRequestException

Unable to CAS write to denylisted partition

Error message

Unable to CAS write to denylisted partition [0x%s] in %s/%s

What it means

StorageProxy.cas rejects compare-and-set (LWT) writes whose partition key is on the partition denylist. When partition denylisting and denylisted-write rejection are enabled, isKeyPermitted() failing causes an InvalidRequestException so the denied write never enters the Paxos protocol.

Solutions

  1. Remove the partition from the denylist (via the denylist JMX/mbean or the operational tooling) so the key is permitted again.
  2. Stop issuing LWT/CAS writes to the denied partition, or redirect the application to a different key.
  3. If denylisting writes is too aggressive, set cassandra.denylist_writes_enabled=false (or the equivalent yaml option) while keeping denylisting for reads only.

Example fix

// before: CAS write to denied partition
UPDATE users USING TTL 60 SET v=? WHERE k=? IF EXISTS;
// after: remove key from denylist first (nodetool/jmx denylist removal), or use non-LWT write
UPDATE users USING TTL 60 SET v=? WHERE k=?;
Defensive patterns

Strategy: validation

Validate before calling

// check denylist state before issuing a CAS (via operator tooling/jmx)
boolean denied = denylistMbean.isKeyPermitted(keyspace, table, key); // if exposed
if (!denied) throw new IllegalStateException("key is denylisted; skip CAS write");

Try / catch

try {
    session.execute(casQuery);
} catch (InvalidRequestException e) {
    if (e.getMessage().contains("denylisted partition")) {
        // alert ops / route write elsewhere; do not blind-retry
    }
}

Prevention

When it happens

Trigger: Executing an INSERT ... IF NOT EXISTS / UPDATE ... IF / DELETE ... IF (CAS/LWT) on a partition that was added to the partition denylist while DatabaseDescriptor.getPartitionDenylistEnabled() and getDenylistWritesEnabled() are true.

Common situations: Operators denylisting a problematic partition (e.g. during incident mitigation or bad-data quarantine) while an application keeps issuing conditional writes to it; keyspace/table names in the denylist entry matching the CAS target.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at src/java/org/apache/cassandra/service/StorageProxy.java:381

     *
     * @return null if the operation succeeds in updating the row, or the current values corresponding to conditions.
     * (since, if the CAS doesn't succeed, it means the current value do not match the conditions).
     */
    public static RowIterator cas(String keyspaceName,
                                  String cfName,
                                  DecoratedKey key,
                                  CASRequest request,
                                  ConsistencyLevel consistencyForPaxos,
                                  ConsistencyLevel consistencyForCommit,
                                  ClientState clientState,
                                  long nowInSeconds,
                                  Dispatcher.RequestTime requestTime)
    throws UnavailableException, IsBootstrappingException, RequestFailureException, RequestTimeoutException, InvalidRequestException, CasWriteUnknownResultException
    {
        if (DatabaseDescriptor.getPartitionDenylistEnabled() && DatabaseDescriptor.getDenylistWritesEnabled() && !partitionDenylist.isKeyPermitted(keyspaceName, cfName, key.getKey()))
        {
            denylistMetrics.incrementWritesRejected();
            throw new InvalidRequestException(String.format("Unable to CAS write to denylisted partition [0x%s] in %s/%s",
                                                            key, keyspaceName, cfName));
        }

        ConsensusAttemptResult lastAttemptResult = null;
        do
        {
            ClusterMetadata cm = ClusterMetadata.current();
            TableMetadata metadata = Schema.instance.validateTable(keyspaceName, cfName);
            ConsensusRoutingDecision decision = consensusRouting(cm, metadata, key, consistencyForPaxos, requestTime, true);
            switch (decision.target)
            {
                case paxosV2:
                    lastAttemptResult = Paxos.cas(key,
                                                  request,
                                                  consistencyForPaxos,
                                                  consistencyForCommit,
                                                  clientState,
                                                  requestTime,

View on GitHub (pinned to 88fd0f6a0e)