dbt-labs/dbt-core · error
expected_field should be Struct
Error message
expected_field should be Struct
What it means
Arrow schema normalization invariant panic in dbt-tracing. normalize_struct_column is only invoked after both the expected and actual fields were verified to be DataType::Struct (the actual check returns an Err above); if the expected field is not Struct at this point, the caller's dispatch logic is wrong. The panic signals a bug in the recursive column-normalization dispatch rather than bad user input.
Source
Thrown at crates/dbt-tracing/src/serialize/arrow.rs:860
fn normalize_struct_column(
path: &str,
array: &ArrayRef,
expected_field: &Field,
actual_field: &Field,
) -> Result<NormalizedColumn, Vec<String>> {
let struct_array = array
.as_any()
.downcast_ref::<StructArray>()
.ok_or_else(|| {
vec![format!(
"field {path}: expected Struct but found {:?}",
array.data_type()
)]
})?;
let DataType::Struct(expected_fields) = expected_field.data_type() else {
unreachable!("expected_field should be Struct");
};
let DataType::Struct(actual_fields) = actual_field.data_type() else {
return Err(vec![format!(
"field {path}: expected Struct but found {:?}",
actual_field.data_type()
)]);
};
let mut child_arrays = Vec::with_capacity(expected_fields.len());
let mut child_fields = Vec::with_capacity(expected_fields.len());
let mut errors = Vec::new();
let mut needs_rebuild = false;
for expected_child in expected_fields.iter() {
let child_path = format!("{path}.{}", expected_child.name());
let Some((child_index, actual_child_field)) = actual_fields
.iter()View on GitHub (pinned to 0267ce9170)
Solutions
- Ensure trace files were produced by a compatible dbt-tracing version
- Check recent changes to normalize_column's dispatch logic for struct fields
- Regenerate the trace file if its schema was manually modified
- Report a bug with the schema that triggers the panic
Example fix
// before
let DataType::Struct(expected_fields) = expected_field.data_type() else {
unreachable!("expected_field should be Struct");
};
// after
let DataType::Struct(expected_fields) = expected_field.data_type() else {
return Err(vec![format!(
"field {path}: expected Struct but found {:?}",
expected_field.data_type()
)]);
}; Defensive patterns
Strategy: validation
Validate before calling
fn is_struct(field: &arrow_schema::Field) -> bool {
matches!(field.data_type(), arrow_schema::DataType::Struct(_))
} Type guard
fn as_struct_fields(dt: &DataType) -> Option<&Fields> {
if let DataType::Struct(f) = dt { Some(f) } else { None }
} Try / catch
if !is_struct(expected_field) {
return Err(vec![format!("field {path}: expected Struct")]);
} Prevention
- Read trace files with a matching dbt-tracing version
- Regenerate traces after schema format changes
- Add round-trip tests for normalize_column over all supported types
When it happens
Trigger: normalize_column dispatching to normalize_struct_column with an expected_field whose data_type is not DataType::Struct.
Common situations: Schema evolution in the trace parquet format; hand-edited or older trace files whose schema diverges from the code's expectations; bugs introduced when adding new nested column types.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Schema not found for canonical FQN: {}
- Parent span must have a SpanStartInfo record in its extensio
- {e}
- Failed to serialize some records: {errors}
- column name normalization preserves schema compatibility
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/f6714108a2ddbf59.
Report an issue: GitHub.