risingwavelabs/risingwave · error

failed to decode persisted `DefaultColumnDesc`

Error message

failed to decode persisted `DefaultColumnDesc`

What it means

`bind_sql_column_constraints` found a persisted `DEFAULT INTERNAL` column default encoded as protobuf bytes, but `DefaultColumnDesc::decode` failed, so the `expect` panics. The stored bytes no longer parse as a valid `DefaultColumnDesc` — a corrupted or format-mismatched catalog entry.

Solutions

  1. Recreate the table/source so the default column descriptor is re-encoded with the current format.
  2. Check version compatibility between the meta store data and the running binary; complete any pending upgrade migration.
  3. If a test/fixture wrote raw bytes, generate them via `DefaultColumnDesc` protobuf encode instead of hand-crafting.

Example fix

// before: hand-written bytes that don't decode
let persisted: Bytes = Bytes::from_static(b"garbage");
// after: encode a real descriptor
let persisted = DefaultColumnDesc { column_id: 1 }.encode_to_vec().into();
Defensive patterns

Strategy: type-guard

Validate before calling

if let Some(persisted) = default_bytes {
    match DefaultColumnDesc::decode(&**persisted) {
        Ok(desc) => { /* proceed */ }
        Err(e) => return Err(anyhow!("corrupt DefaultColumnDesc in catalog: {}", e)),
    }
}

Type guard

fn decodable_default(b: &[u8]) -> Option<DefaultColumnDesc> { DefaultColumnDesc::decode(b).ok() }

Try / catch

match DefaultColumnDesc::decode(&**persisted) {
    Ok(desc) => apply(desc),
    Err(e) => log::error!("failed to decode DefaultColumnDesc: {e}"),
}

Prevention

When it happens

Trigger: CREATE TABLE/SOURCE (via `bind_create_source_or_table_with_connector`, `gen_create_table_plan_without_source`, `gen_create_table_plan_for_cdc_table`) reading a column whose `generated_or_default_column` holds protobuf bytes that fail protobuf decoding — e.g. bytes written by an incompatible older schema version or manually corrupted meta storage.

Common situations: Rolling upgrades where the persisted proto definition changed; restoring state from backups of a different version; meta-store corruption.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/4b5cc8caea9fd8e9. Report an issue: GitHub.

Appendix: source

Thrown at src/frontend/src/handler/create_table.rs:353

                            column.name
                        ))
                        .into());
                    }
                }
                ColumnOption::DefaultValueInternal { persisted, expr: _ } => {
                    // When a `DEFAULT INTERNAL` is used internally for schema change, the persisted value
                    // should already be set during purifcation. So if we encounter an empty value here, it
                    // means the user has specified it explicitly in the SQL statement, typically by
                    // directly copying the result of `SHOW CREATE TABLE` and executing it.
                    if persisted.is_empty() {
                        bail_bind_error!(
                            "DEFAULT INTERNAL is only used for internal purposes, \
                             please specify a concrete default value"
                        );
                    }

                    let desc = DefaultColumnDesc::decode(&**persisted)
                        .expect("failed to decode persisted `DefaultColumnDesc`");

                    column_catalogs[idx].column_desc.generated_or_default_column =
                        Some(GeneratedOrDefaultColumn::DefaultColumn(desc));
                }
                _ => {}
            }
        }
    }
    Ok(())
}

/// Currently we only support Primary key table constraint, so just return pk names if it exists
pub fn bind_table_constraints(table_constraints: &[TableConstraint]) -> Result<Vec<String>> {
    let mut pk_column_names = vec![];

    for constraint in table_constraints {
        match constraint {
            TableConstraint::Unique {

View on GitHub (pinned to 6469eb736d)