apache/cassandra · error · InvalidRequestException

Modification is not supported by table %s

Error message

Modification is not supported by table %s

What it means

AbstractLazyVirtualTable represents read-only lazily-computed virtual tables (e.g. caches stats, transactions). Virtual tables have no storage backing, so apply(PartitionUpdate) for INSERT/UPDATE/DELETE is rejected up front with InvalidRequestException rather than silently ignoring mutations.

Source

Thrown at src/java/org/apache/cassandra/db/virtual/AbstractLazyVirtualTable.java:774

        PartitionsCollector collector = collector(dataRange, columnFilter, rowFilter, limits);
        try
        {
            collect(collector);
        }
        catch (InternalDoneException ignore) {}
        catch (InternalTimeoutException ignore)
        {
            if (onTimeout != OnTimeout.BEST_EFFORT || collector.isEmpty())
                throw new ReadTimeoutException(ONE, 0, 1, false);
            ClientWarn.instance.warn("Ran out of time. Returning best effort.");
        }
        return collector.finish();
    }

    @Override
    public void apply(PartitionUpdate update)
    {
        throw new InvalidRequestException("Modification is not supported by table " + metadata);
    }

    @Override
    public void truncate()
    {
        throw new InvalidRequestException("Truncation is not supported by table " + metadata);
    }

    @Override
    public String toString()
    {
        return metadata().toString();
    }

    static Object[] composePartitionKeys(DecoratedKey decoratedKey, TableMetadata metadata)
    {
        if (metadata.partitionKeyColumns().size() == 1)
            return new Object[] { metadata.partitionKeyType.compose(decoratedKey.getKey()) };

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Do not mutate virtual tables — they reflect live in-memory state and are read-only
  2. If the goal is to change runtime state, use the corresponding real mechanism (nodetool commands, system tables designed for writes, or configuration changes)
  3. Point the statement at the intended non-virtual table (check the keyspace: system_views vs system)

Example fix

// before
DELETE FROM system_views.clients WHERE address = '127.0.0.1';
// after
// use nodetool or the appropriate operational command instead
$ nodetool disablebinary  // example: change state via tooling, not DML
Defensive patterns

Strategy: validation

Validate before calling

if (keyspace.startsWith("system_views") || metadata.isVirtual()) throw new UnsupportedOperationException("virtual tables are read-only");

Type guard

boolean writable = t -> !t.getMetadata().isVirtual();

Try / catch

try { session.execute(dml); } catch (InvalidRequestException e) { if (e.getMessage().startsWith("Modification is not supported")) log.error("read-only virtual table"); else throw e; }

Prevention

When it happens

Trigger: Executing INSERT, UPDATE, DELETE, or BATCH containing mutations against a read-only virtual table, e.g. `DELETE FROM system_views.clients` or `INSERT INTO system.caches (...)`.

Common situations: Scripts written for normal tables reused against system_views tables; automation trying to 'clean up' virtual table rows; mistaken use of system vs system_views tables.

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