apache/cassandra · error · InvalidRequestException

Cannot add a column '%s' of type %s, incompatible with previ

Error message

Cannot add a column '%s' of type %s, incompatible with previously dropped column '%s' of type %s

What it means

After a column is dropped, Cassandra remembers its type so it can deserialize any lingering data. Re-adding a column of the same name with a type that is not serialization-compatible with the dropped type is rejected (CASSANDRA-8099), because reading old sstables could produce corrupted or misinterpreted values, especially under schema races.

Source

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

            // 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
                // columns is pushed deeper down the line. The latter would still be problematic in cases of schema races.
                if (!type.isSerializationCompatibleWith(droppedColumn.type))
                {
                    throw ire("Cannot add a column '%s' of type %s, incompatible with previously dropped column '%s' of type %s",
                              name,
                              type.asCQL3Type(),
                              name,
                              droppedColumn.type.asCQL3Type());
                }

                if (droppedColumn.isStatic() != isStatic)
                {
                    throw ire("Cannot re-add previously dropped column '%s' of kind %s, incompatible with previous kind %s",
                              name,
                              isStatic ? ColumnMetadata.Kind.STATIC : ColumnMetadata.Kind.REGULAR,
                              droppedColumn.kind);
                }

                // Cannot re-add a dropped counter column. See #7831.
                if (table.isCounter())
                    throw ire("Cannot re-add previously dropped counter column %s", name);
            }

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Use a new column name instead of reusing the dropped one, e.g. ALTER TABLE t ADD col2 <type>.
  2. Re-add the column with exactly the same type (or a serialization-compatible one) as when it was dropped.
  3. Check the dropped column's recorded type via system_schema.columns / sstable metadata (system.dropped_columns) to match it.
  4. If the old data is fully expired (GC grace passed) and the table history cleared, recreate the table to purge the dropped-column record, then add the column with the new type.

Example fix

// before
ALTER TABLE t DROP cnt;
ALTER TABLE t ADD cnt text;   -- fails: was int
// after
ALTER TABLE t DROP cnt;
ALTER TABLE t ADD cnt_text text;  -- new name
-- or: ALTER TABLE t ADD cnt int;  -- same, compatible type
Defensive patterns

Strategy: validation

Validate before calling

// before re-adding a dropped column name
ColumnMetadata dropped = table.getDroppedColumn(ByteBufferUtil.bytes(colName));
if (dropped != null && !newType.isSerializationCompatibleWith(dropped.type))
    throw new IllegalArgumentException("Type " + newType.asCQL3Type() + " incompatible with dropped column of " + dropped.type.asCQL3Type());

Type guard

boolean canReAdd(TableMetadata t, String name, AbstractType<?> type) {
    ColumnMetadata d = t.getDroppedColumn(UTF8Type.instance.decompose(name));
    return d == null || type.isSerializationCompatibleWith(d.type);
}

Try / catch

try {
    session.execute(alterAdd);
} catch (InvalidQueryException e) {
    if (e.getMessage().contains("incompatible with previously dropped column"))
        throw new SchemaConflictException("Choose a new column name or the original type", e);
    throw e;
}

Prevention

When it happens

Trigger: ALTER TABLE t DROP col; then ALTER TABLE t ADD col <different_type> where the new type fails AbstractType.isSerializationCompatibleWith(dropped type) — e.g. int dropped, text re-added; or same name with an incompatible non-frozen/frozen collection change.

Common situations: Reusing a dropped column name with a different type; schema-sync tools renaming/re-typing a column by drop+add; migrating after a type change decision; accidental reuse of a name from long ago that is still in the dropped-columns history.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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