apache/cassandra · error · InvalidRequestException

Unable to write to denylisted partition

Error message

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

What it means

During mutation dispatch (StorageProxy's mutates via StorageProxy/performWrite path), each mutation's partition key is checked against the partition denylist; a denied key causes the write to be rejected with an InvalidRequestException naming the denied partition and its keyspace/table. This is an operator-controlled data quarantine feature.

Solutions

  1. Remove the partition from the denylist via the denylist mbean/tooling once the issue is resolved.
  2. Change the application to stop writing to the denied partition key.
  3. Set denylist_writes_enabled=false in cassandra.yaml if writes should be allowed while reads remain denied.
  4. Confirm with the cluster operator that the denylist entry is intentional before working around it.

Example fix

// before
session.execute("INSERT INTO flagged (k, v) VALUES (?, ?)", badKey, v); // InvalidRequestException
// after: operator removes key from denylist (nodetool/jmx), then retry the write
session.execute("INSERT INTO flagged (k, v) VALUES (?, ?)", badKey, v);
Defensive patterns

Strategy: validation

Validate before calling

// application-side denylist mirror
if (denylistCache.contains(partitionKey)) {
    throw new IllegalArgumentException("partition " + partitionKey + " is denylisted; refusing write");
}

Try / catch

try {
    session.execute(write);
} catch (InvalidRequestException e) {
    if (e.getMessage().contains("denylisted partition")) {
        // quarantine locally, alert ops, do not retry until entry removed
    }
}

Prevention

When it happens

Trigger: Any standard (non-CAS) write (INSERT/UPDATE/DELETE/BATCH member) whose partition key is denylisted while partition_denylist_enabled and denylist_writes_enabled are true.

Common situations: Operator denylisted a partition during an incident (corrupt data, runaway writer) while an application keeps writing to it; denylist entries added via JMX persist while clients are unaware.

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/8da5de09810d6c9c. Report an issue: GitHub.

Appendix: source

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

                                          ConsistencyLevel consistencyLevel,
                                          boolean mutateAtomically,
                                          Dispatcher.RequestTime requestTime,
                                          PreserveTimestamp preserveTimestamps)
    throws WriteTimeoutException, WriteFailureException, UnavailableException, OverloadedException, InvalidRequestException
    {
        if (DatabaseDescriptor.getPartitionDenylistEnabled() && DatabaseDescriptor.getDenylistWritesEnabled())
        {
            for (final IMutation mutation : mutations)
            {
                for (final TableId tid : mutation.getTableIds())
                {
                    if (!partitionDenylist.isKeyPermitted(tid, mutation.key().getKey()))
                    {
                        denylistMetrics.incrementWritesRejected();
                        // While Schema.instance.getTableMetadata() can return a null value, in this case the isKeyPermitted
                        // call above ensures that we cannot have a null associated tid at this point.
                        final TableMetadata tmd = Schema.instance.getTableMetadata(tid);
                        throw new InvalidRequestException(String.format("Unable to write to denylisted partition [0x%s] in %s/%s",
                                                                        mutation.key().toString(), tmd.keyspace, tmd.name));
                    }
                }
            }
        }

        List<Mutation> augmented = TriggerExecutor.instance.execute(mutations);

        String keyspaceName = mutations.iterator().next().getKeyspaceName();
        boolean updatesView = Keyspace.open(keyspaceName)
                              .viewManager
                              .updatesAffectView(mutations, true);

        long size = IMutation.dataSize(augmented != null ? augmented : mutations);
        writeMetrics.mutationSize.update(size);
        writeMetricsForLevel(consistencyLevel).mutationSize.update(size);
        if (augmented != null || mutateAtomically || updatesView)
            mutateAtomically(augmented != null ? augmented : (List<Mutation>)mutations, consistencyLevel, updatesView, requestTime);

View on GitHub (pinned to 88fd0f6a0e)