risingwavelabs/risingwave · error
Root schema of debezium shall be a record but got: {root:?}
Error message
Root schema of debezium shall be a record but got: {root:?} What it means
Debezium Avro events wrap the row payload in a `before`/`after` structure. extract_debezium_table_schema requires the root Avro schema to be a Record so it can look up the `before` field; any other root schema kind (union, array, map, primitive) is rejected.
Source
Thrown at src/connector/src/parser/debezium/avro_parser.rs:176
// - op
// - ts_ms
// - transaction
// See <https://debezium.io/documentation/reference/stable/connectors/mysql.html#mysql-events>
avro_schema_to_fields(
// This assumes no external `Ref`s (e.g. "before" referring to "after" or "source").
// Internal `Ref`s inside the "before" tree are allowed.
extract_debezium_table_schema(&self.outer_schema)?,
// TODO: do we need to support map type here?
None,
)
.map_err(Into::into)
}
}
fn extract_debezium_table_schema(root: &Schema) -> anyhow::Result<&Schema> {
let Schema::Record(root_record) = root else {
anyhow::bail!("Root schema of debezium shall be a record but got: {root:?}");
};
let idx = (root_record.lookup.get("before"))
.context("Root schema of debezium shall contain \"before\" field.")?;
let schema = &root_record.fields[*idx].schema;
// It is wrapped inside a union to allow null, so we look inside.
let Schema::Union(union_schema) = schema else {
return Ok(schema);
};
get_nullable_union_inner(union_schema).context(format!(
"illegal avro union schema, expected [null, T], got {:?}",
union_schema
))
}
#[cfg(test)]
mod tests {
use std::io::Read;
use std::path::PathBuf;View on GitHub (pinned to 6469eb736d)
Solutions
- Ensure the source actually emits Debezium-encoded Avro whose root is the Debezium envelope record containing before/after
- Check the schema registry subject resolution (subject naming strategy) so the correct envelope schema is fetched
- If consuming a plain Avro stream, use the non-Debezium Avro parser instead
Defensive patterns
Strategy: validation
Validate before calling
// JS pseudo-guard against a fetched Avro schema
if (schema.type !== 'record' || !schema.fields.some(f => f.name === 'before')) throw new Error('not a Debezium Avro envelope'); Try / catch
if err.to_string().contains("Root schema of debezium shall be a record") { verify_schema_registry_subject(); } else { propagate } Prevention
- Verify the registry subject returns the Debezium envelope schema, not a value schema
- Inspect one message with avro-tools before creating the source
- Use the plain Avro parser for non-Debezium streams
When it happens
Trigger: Feeding a Debezium Avro parser a schema whose top level is not a record — e.g. an Avro union, array, map, enum, or primitive schema.
Common situations: Pointing the Debezium parser at a non-Debezium Avro stream; a misconfigured schema registry subject returning a wrapped/different root schema; hand-rolled Avro producers that don't follow Debezium envelope layout.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- schema invalid, record type required at top level of the sch
- circular reference detected in Avro schema: {} -> {}
- failed to convert JSON schema to Avro schema: {}
- Non-zero compression {} not supported
- Debezium Mongo needs a `_id` column in table
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/35d66413542415c9.
Report an issue: GitHub.