databendlabs/databend · error

DataType::Map should contain a struct field child

Error message

DataType::Map should contain a struct field child

What it means

This is an internal invariant panic in the Delta Lake storage's Arrow 5.6 type conversion layer. When converting an Arrow Map type to Databend's DataType, the converter expects the Map's single field child to be a Struct containing exactly the key and value fields; any Arrow schema that violates this (e.g. a Map whose child is not a Struct) triggers an immediate panic via unreachable!() instead of a recoverable error.

Solutions

  1. Inspect the offending Arrow schema and confirm the Map field's child is a Struct with key and value entries
  2. Fix the upstream writer or regenerate the Delta table so Map columns use the standard struct(key, value) child layout
  3. Replace the panic with a proper Err(Arrow56ConversionError::InvalidDataType(...)) so bad schemas surface as a readable error instead of aborting the query
  4. Pin/align the arrow crate versions used to write and read the data

Example fix

// before
} else {
    panic!("DataType::Map should contain a struct field child");
}
// after
} else {
    return Err(Arrow56ConversionError::InvalidDataType(format!(
        "DataType::Map should contain a struct field child, got {:?}",
        field.data_type()
    )));
}
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_arrow_map(field: &ArrowField) -> bool {
    matches!(field.data_type(), ArrowDataType::Struct(children) if children.len() >= 2)
}
if !is_valid_arrow_map(&field) {
    return Err(Arrow56ConversionError::InvalidDataType(
        "Map field child is not a struct".into(),
    ));
}

Type guard

fn is_struct_type(dt: &ArrowDataType) -> bool {
    matches!(dt, ArrowDataType::Struct(_))
}

Try / catch

// Panics cannot be caught idiomatically in Rust; prefer validating before conversion.
let result = std::panic::catch_unwind(|| DataType::try_from_value(&field));
match result {
    Ok(Ok(dt)) => use_datatype(dt),
    Ok(Err(e)) => report_conversion_error(e),
    Err(_) => report_schema_bug(),
}

Prevention

When it happens

Trigger: Calling DataType::try_from_value on an Arrow Field whose data_type is ArrowDataType::Map when field.data_type() does not match ArrowDataType::Struct(_) — e.g. reading a Delta table whose Parquet/Arrow schema encodes Map with a malformed or non-struct child field.

Common situations: Ingesting a Delta table written by another engine or library version that serializes Map columns differently; corrupted or hand-crafted Parquet metadata; a version mismatch between the arrow crate that wrote the data and the arrow56 shim used by the converter.

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


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/2145b99d8262411c. Report an issue: GitHub.

Appendix: source

Thrown at src/query/storages/delta/src/arrow56_conversion.rs:310

            .into()),
            ArrowDataType::LargeListView(field) => Ok(ArrayType::new(
                (*field).data_type().try_into_value()?,
                (*field).is_nullable(),
            )
            .into()),
            ArrowDataType::FixedSizeList(field, _) => Ok(ArrayType::new(
                (*field).data_type().try_into_value()?,
                (*field).is_nullable(),
            )
            .into()),
            ArrowDataType::Map(field, _) => {
                if let ArrowDataType::Struct(struct_fields) = field.data_type() {
                    let key_type = DataType::try_from_value(struct_fields[0].data_type())?;
                    let value_type = DataType::try_from_value(struct_fields[1].data_type())?;
                    let value_type_nullable = struct_fields[1].is_nullable();
                    Ok(MapType::new(key_type, value_type, value_type_nullable).into())
                } else {
                    panic!("DataType::Map should contain a struct field child");
                }
            }
            // Dictionary types are just an optimized in-memory representation of an array.
            // Schema-wise, they are the same as the value type.
            ArrowDataType::Dictionary(_, value_type) => Ok(value_type.as_ref().try_into_value()?),
            s => Err(Arrow56ConversionError::InvalidDataType(format!(
                "Invalid data type for Delta Lake: {s}"
            ))),
        }
    }
}

View on GitHub (pinned to 288d84d76e)