{"record":{"id":"fae141f8086d9b11","repo":"apache/cassandra","slug":"cannot-re-add-previously-dropped-column-s-of-ki","errorCode":null,"errorMessage":"Cannot re-add previously dropped column '%s' of kind %s, incompatible with previous kind %s","messagePattern":"Cannot re-add previously dropped column '(.+?)' of kind (.+?), incompatible with previous kind (.+?)","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java","lineNumber":395,"sourceCode":"            }\n\n            ColumnMetadata droppedColumn = table.getDroppedColumn(name.bytes);\n            if (null != droppedColumn)\n            {\n                // After #8099, not safe to re-add columns of incompatible types - until *maybe* deser logic with dropped\n                // columns is pushed deeper down the line. The latter would still be problematic in cases of schema races.\n                if (!type.isSerializationCompatibleWith(droppedColumn.type))\n                {\n                    throw ire(\"Cannot add a column '%s' of type %s, incompatible with previously dropped column '%s' of type %s\",\n                              name,\n                              type.asCQL3Type(),\n                              name,\n                              droppedColumn.type.asCQL3Type());\n                }\n\n                if (droppedColumn.isStatic() != isStatic)\n                {\n                    throw ire(\"Cannot re-add previously dropped column '%s' of kind %s, incompatible with previous kind %s\",\n                              name,\n                              isStatic ? ColumnMetadata.Kind.STATIC : ColumnMetadata.Kind.REGULAR,\n                              droppedColumn.kind);\n                }\n\n                // Cannot re-add a dropped counter column. See #7831.\n                if (table.isCounter())\n                    throw ire(\"Cannot re-add previously dropped counter column %s\", name);\n            }\n\n            if (isStatic)\n                tableBuilder.addStaticColumn(name, type, mask, columnConstraints);\n            else\n                tableBuilder.addRegularColumn(name, type, mask, columnConstraints);\n\n            if (!isStatic)\n            {\n                for (ViewMetadata view : keyspace.views.forTable(table.id))","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java#L377-L413","documentation":"When re-adding a previously dropped column, its kind (REGULAR vs STATIC) must match the kind it had when dropped, because old rows' static cells would be misread as regular cells (or vice versa) if the kind flips. Cassandra compares the isStatic flag of the new column against the stored dropped ColumnMetadata and rejects a mismatch.","triggerScenarios":"ALTER TABLE t DROP col; then ALTER TABLE t ADD col <type> STATIC (or vice versa) on a table with clustering columns, where the dropped ColumnMetadata records the opposite kind.","commonSituations":"Toggling a column between regular and static after a drop; schema-migration tools converting a regular column to static by drop+add; developer experiments on tables whose dropped-column history still contains the old kind.","solutions":["Re-add the column with the same kind it had before dropping (omit STATIC if it was REGULAR; include STATIC if it was STATIC).","If a kind change is required, use a new column name and migrate data with an INSERT/UPDATE copy, then drop the old column.","Drop-and-recreate the table (after data migration) if the dropped-column history must be cleared to permit the new layout.","Verify prior kind via system.dropped_columns (dropped_at/dropped metadata) before re-adding."],"exampleFix":"// before\nALTER TABLE t DROP location;   -- was STATIC\nALTER TABLE t ADD location text;            -- kind mismatch (REGULAR)\n// after\nALTER TABLE t ADD location text STATIC;     -- same kind as dropped\n-- or use a new name: ALTER TABLE t ADD location2 text;","handlingStrategy":"validation","validationCode":"// before re-adding, ensure the kind matches what was dropped\nColumnMetadata dropped = table.getDroppedColumn(ByteBufferUtil.bytes(colName));\nif (dropped != null && dropped.isStatic() != isStaticRequested)\n    throw new IllegalArgumentException(\"Kind mismatch: dropped column was \" + dropped.kind + \"; re-add with the same kind.\");","typeGuard":"boolean kindMatches(TableMetadata t, String name, boolean isStatic) {\n    ColumnMetadata d = t.getDroppedColumn(UTF8Type.instance.decompose(name));\n    return d == null || d.isStatic() == isStatic;\n}","tryCatchPattern":"try {\n    session.execute(alterAdd);\n} catch (InvalidQueryException e) {\n    if (e.getMessage().contains(\"incompatible with previous kind\"))\n        throw new SchemaConflictException(\"Re-add the column with its original REGULAR/STATIC kind\", e);\n    throw e;\n}","preventionTips":["Record column kind in your schema migration metadata so drop+add cycles restore the same kind.","Avoid converting columns between REGULAR and STATIC via drop+add; create a new column instead.","Inspect system.dropped_columns before any ADD that reuses an old name.","Keep clustering-column design stable so STATIC columns stay meaningful."],"tags":["cql","schema","alter-table","static-column","dropped-column"],"backgroundTag":"schema-validation-failed","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}