apache/cassandra · error · UnauthorizedException

keyspace is not user-modifiable.

Error message

${keyspace} keyspace is not user-modifiable.

What it means

Cassandra throws this UnauthorizedException when a client attempts DDL (ALTER, DROP, or CREATE) against a local system keyspace (e.g. system, system_schema, system_auth). Local system keyspaces are fully managed internally and their schema must never be modified by users, so ClientState blocks the operation before permission checks proceed.

Solutions

  1. Exclude all local system keyspaces (SchemaConstants.listLocalSystemKeyspaces / Schema.SYSTEM_KEYSPACE_NAMES) from your DDL scripts and migration tooling.
  2. If you meant to modify application data, target your own keyspace instead of a system one.
  3. If you need to change replication of replicated system keyspaces (system_auth, system_distributed, system_traces), use ALTER KEYSPACE with ALTER permission on a keyspace-level resource — those are handled separately.
  4. To change internals, adjust cassandra.yaml or use nodetool/JMX instead of CQL DDL on system keyspaces.

Example fix

// before
ALTER TABLE system.local ADD extra text;
// after
ALTER TABLE my_app.settings ADD extra text;
Defensive patterns

Strategy: validation

Validate before calling

import org.apache.cassandra.db.SchemaConstants;
if (SchemaConstants.isLocalSystemKeyspace(keyspace))
    throw new IllegalStateException("Refusing DDL on local system keyspace " + keyspace);

Prevention

When it happens

Trigger: Executing CREATE/ALTER/DROP statements (or calling ClientState.ensurePermission for DDL permissions) where the target keyspace is one returned by SchemaConstants.isLocalSystemKeyspace, e.g. ALTER TABLE system.local ..., DROP KEYSPACE system_auth, or CREATE TABLE in system_schema.

Common situations: Running migration scripts or ORM auto-schema tools that blindly iterate over all keyspaces including system ones; copying application schema DDL against a cluster with system keyspaces present; attempting to 'clean up' system keyspaces manually; tools that snapshot/restore by re-creating all keyspaces.

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

Appendix: source

Thrown at src/java/org/apache/cassandra/service/ClientState.java:587

        for (IResource r : resources)
            if (authorize(r).contains(perm))
                return;

        throw new UnauthorizedException(String.format("User %s has no %s permission on %s or any of its parents",
                                                      user.getName(),
                                                      perm,
                                                      resource));
    }

    private void preventSystemKSSchemaModification(String keyspace, DataResource resource, Permission perm)
    {
        // we only care about DDL statements
        if (perm != Permission.ALTER && perm != Permission.DROP && perm != Permission.CREATE)
            return;

        // prevent ALL local system keyspace modification
        if (SchemaConstants.isLocalSystemKeyspace(keyspace))
            throw new UnauthorizedException(keyspace + " keyspace is not user-modifiable.");

        if (SchemaConstants.isReplicatedSystemKeyspace(keyspace))
        {
            // allow users with sufficient privileges to alter replication params of replicated system keyspaces
            if (perm == Permission.ALTER && resource.isKeyspaceLevel())
                return;

            // prevent all other modifications of replicated system keyspaces
            throw new UnauthorizedException(String.format("Cannot %s %s", perm, resource));
        }
    }

    public void validateLogin()
    {
        if (user == null)
        {
            throw new UnauthorizedException("You have not logged in");
        }

View on GitHub (pinned to 88fd0f6a0e)