risingwavelabs/risingwave · error
column "{}" was persisted with legacy encoding thus cannot b
Error message
column "{}" was persisted with legacy encoding thus cannot be altered, consider dropping and readding the column What it means
`ColIdGenerator::generate` refuses to ALTER a column whose original data type was persisted with legacy encoding: `DataType::can_alter()` returned `Some(false)`. Changing such a column's type in place would produce data unreadable by the new encoding, so the operation is rejected with guidance to drop and re-add the column.
Source
Thrown at src/frontend/src/handler/create_table/col_id_gen.rs:168
/// Generate [`ColumnId`]s for the given column and its nested fields (if any) recursively.
/// The IDs of nested fields will be reflected in the updated [`DataType`] of the column.
///
/// Returns an error if there's incompatible data type change.
pub fn generate(&mut self, col: &mut ColumnCatalog) -> Result<()> {
let mut path = vec![Segment::Field(col.name().to_owned())];
if let Some((original_column_id, original_data_type)) = self.existing.get(&path) {
if original_data_type == col.data_type() {
col.column_desc.column_id = *original_column_id;
// Equality above ignores nested field IDs. We need to clone them below.
col.column_desc.data_type = original_data_type.clone();
return Ok(());
} else {
// Check if the column can be altered.
match original_data_type.can_alter() {
Some(true) => { /* pass */ }
Some(false) => bail!(
"column \"{}\" was persisted with legacy encoding thus cannot be altered, \
consider dropping and readding the column",
col.name()
),
None => bail!(
"column \"{}\" cannot be altered; only types containing struct can be altered",
col.name()
),
}
}
}
fn handle(
this: &mut ColumnIdGenerator,
path: &mut Path,
data_type: DataType,
) -> Result<(ColumnId, DataType)> {
macro_rules! with_segment {View on GitHub (pinned to 6469eb736d)
Solutions
- Follow the message: `ALTER TABLE t DROP COLUMN c;` then `ALTER TABLE t ADD COLUMN c <new_type>;`, backfilling data if needed.
- Create a new column with the desired type, copy values with a cast via a batch UPDATE/INSERT, then drop the old column.
- If legacy encoding is no longer needed, recreate the table and stream data into it.
Example fix
-- before (rejected) ALTER TABLE t ALTER COLUMN s TYPE struct<a int>; -- after ALTER TABLE t ADD COLUMN s_new struct<a int>; UPDATE t SET s_new = s; ALTER TABLE t DROP COLUMN s; ALTER TABLE t RENAME COLUMN s_new TO s;
Defensive patterns
Strategy: validation
Validate before calling
fn check_alterable(original: &DataType, new: &DataType) -> Result<(), String> {
match original.can_alter() {
Some(true) => Ok(()),
Some(false) => Err("column uses legacy encoding; drop and re-add instead".into()),
None => Err("only struct-containing types can be altered".into()),
}
} Prevention
- Check `DataType::can_alter()` semantics before planning ALTER COLUMN TYPE
- Prefer add-new-column + migrate + drop pattern for legacy columns
- Review release notes on encoding changes before altering old tables
When it happens
Trigger: `ALTER TABLE ... ALTER COLUMN c TYPE ...` where the target type is struct-containing-alterable but the column's original type was persisted before the current encoding (e.g. older struct types), making in-place type alteration unsafe.
Common situations: Tables created on an older RisingWave version being altered after an upgrade; migration scripts that alter legacy columns 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
- duplicate path: {:?}
- version field not set
- column "{}" cannot be altered; only types containing struct
- incompatible data type change from {:?} to {:?} at path "{}"
- Join key types are not aligned: LHS: {outer_type:?}, RHS: {i
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/11003b36210bc4ea.
Report an issue: GitHub.