databendlabs/databend · error

failed to build jsonb object from tuple

Error message

failed to build jsonb object from tuple

What it means

This panic comes from an `expect` on `OwnedJsonb::build_object` while converting a Tuple scalar into a JSONB variant during `cast_scalar_to_variant`. The library assumes that building a JSON object from the tuple's field names and values cannot fail; the expect is an internal invariant check. If it fires, the jsonb builder rejected the object construction (e.g. an unencodable raw value or an internal builder error).

Solutions

  1. Verify the tuple type's field names length matches the number of values passed to `cast_scalar_to_variant` (fields_name.len() == values.len()).
  2. Re-check the source block's declared tuple schema against the actual column schema; refresh any cached schema/metas.
  3. Reproduce with the specific tuple value and report to Databend with the value and cast path (this is an intended-to-be-impossible path).
  4. As a stopgap, replace the `expect` with proper error propagation (`map_err(ErrorCode::Internal)`) in a patched build to get a diagnosable error instead of a panic.

Example fix

// before
OwnedJsonb::build_object(...).expect("failed to build jsonb object from tuple")
// after
OwnedJsonb::build_object(...).map_err(|e| ErrorCode::Internal(format!("failed to build jsonb object from tuple: {e}")))?
Defensive patterns

Strategy: validation

Validate before calling

if fields_name.len() != values.len() {
    return Err(ErrorCode::Internal(format!(
        "tuple field names ({}) != values ({})",
        fields_name.len(), values.len()
    )));
}

Type guard

fn tuple_fields_match(names: &[String], values: &[ScalarRef]) -> bool { names.len() == values.len() }

Prevention

When it happens

Trigger: Calling `cast_scalar_to_variant` on a `ScalarRef::Tuple` whose field count mismatches `fields_name` length (index out of range while mapping names), or where one of the field byte buffers cannot be embedded by `OwnedJsonb::build_object`. Reached via array_construct/object-style casts or `merge_result` aggregation of tuple values.

Common situations: Schema drift where a table's tuple type was altered but cached field names were not refreshed; mixing Databend versions where tuple encoding changed; corrupt or hand-crafted block data whose tuple payload does not match its declared type.

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/654721c101a11e82. Report an issue: GitHub.

Appendix: source

Thrown at src/query/expression/src/types/variant.rs:377

                    let iter = fields.into_iter();
                    let mut builder = BinaryColumnBuilder::with_capacity(iter.size_hint().0, 0);
                    for (scalar, typ) in iter.zip(fields_type) {
                        cast_scalar_to_variant(
                            scalar,
                            tz,
                            &mut builder.data,
                            Some(&typ.remove_nullable()),
                        );
                        builder.commit_row();
                    }
                    let values = builder.build();
                    OwnedJsonb::build_object(
                        values
                            .iter()
                            .enumerate()
                            .map(|(i, bytes)| (fields_name[i].clone(), RawJsonb::new(bytes))),
                    )
                    .expect("failed to build jsonb object from tuple")
                }
                _ => {
                    let values = cast_scalars_to_variants(fields, tz, None);
                    OwnedJsonb::build_object(
                        values
                            .iter()
                            .enumerate()
                            .map(|(i, bytes)| (format!("{}", i + 1), RawJsonb::new(bytes))),
                    )
                    .expect("failed to build jsonb object from tuple")
                }
            };
            buf.extend_from_slice(owned_jsonb.as_ref());
            return;
        }
        ScalarRef::Variant(bytes) => {
            buf.extend_from_slice(bytes);
            return;

View on GitHub (pinned to 288d84d76e)