apache/cassandra · error · InvalidRequestException

Cannot add new column to a COMPACT STORAGE table

Error message

Cannot add new column to a COMPACT STORAGE table

What it means

Tables created WITH COMPACT STORAGE use the legacy Thrift-compatible storage format with a fixed column set; adding new columns via ALTER TABLE is rejected. Compact tables cannot gain new non-primary columns after creation.

Solutions

  1. Migrate the table to a regular (non-compact) schema: create a new table without COMPACT STORAGE and copy data across
  2. If the legacy schema must stay, add needed data via the existing sparse column encoding or a companion table
  3. Check table params: SELECT * FROM system_schema.tables WHERE keyspace_name=? AND table_name=? (compact_storage flag) before scripting DDL

Example fix

// before
session.execute("ALTER TABLE my_app.legacy_metrics ADD description text");
// after
session.execute("CREATE TABLE my_app.metrics (id uuid PRIMARY KEY, value blob, description text)");
// then copy data from legacy_metrics into metrics and drop the compact table
Defensive patterns

Strategy: validation

Validate before calling

Row r = session.execute("SELECT * FROM system_schema.tables WHERE keyspace_name=? AND table_name=?", ks, table).one();
if (r != null && r.getBoolean("compact_storage"))
    throw new IllegalStateException("Cannot ADD columns to a COMPACT STORAGE table");

Try / catch

try {
    session.execute(alter);
} catch (InvalidRequestException e) {
    if (e.getMessage().contains("COMPACT STORAGE"))
        log.error("Legacy compact table requires migration before schema change");
    else throw e;
}

Prevention

When it happens

Trigger: ALTER TABLE ... ADD on a table where table.isCompactTable() is true — i.e. tables created with WITH COMPACT STORAGE, typically legacy tables migrated from Thrift

Common situations: Operating on legacy Thrift-era tables; applying generic schema migration scripts (written for regular tables) against compact-storage tables; attempting to extend an old data model in place.

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

Appendix: source

Thrown at src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java:364

                               Views.Builder viewsBuilder)
        {
            ColumnIdentifier name = column.name;
            AbstractType<?> type = column.type.prepare(keyspaceName, keyspace.types).getType();
            boolean isStatic = column.isStatic;
            ColumnMask mask = column.mask == null ? null : column.mask.prepare(keyspaceName, tableName, name, type, keyspace.userFunctions);
            ColumnConstraints columnConstraints = column.constraints == null ? ColumnConstraints.NO_OP : column.constraints.prepare(name);

            if (null != tableBuilder.getColumn(name)) {
                if (!ifColumnNotExists)
                    throw ire("Column with name '%s' already exists", name);
                return;
            }

            if (type.isCounter() && (table.params.transactionalMode.accordIsEnabled || table.params.transactionalMigrationFrom.migratingFromAccord()))
                throw ire(format(ACCORD_COUNTER_COLUMN_UNSUPPORTED, keyspaceName, tableName, table.params.transactionalMode, table.params.transactionalMigrationFrom));

            if (table.isCompactTable())
                throw ire("Cannot add new column to a COMPACT STORAGE table");

            if (isStatic && table.clusteringColumns().isEmpty())
                throw ire("Static columns are only useful (and thus allowed) if the table has at least one clustering column");

            // check for nested non-frozen UDTs or collections in a non-frozen UDT
            if (type.isUDT() && type.isMultiCell())
            {
                for (AbstractType<?> fieldType : ((UserType) type).fieldTypes())
                {
                    if (fieldType.isMultiCell())
                        throw ire("Non-frozen UDTs with nested non-frozen collections are not supported for column " + column.name);
                }
            }

            ColumnMetadata droppedColumn = table.getDroppedColumn(name.bytes);
            if (null != droppedColumn)
            {
                // After #8099, not safe to re-add columns of incompatible types - until *maybe* deser logic with dropped

View on GitHub (pinned to 88fd0f6a0e)