databendlabs/databend · critical

map inner type must be a tuple

Error message

map inner type must be a tuple

What it means

When resolving map access of the form map['key'] or map[key], the planner expects the Map's inner type to be a TUPLE(key_type, value_type) so it can extract the value type. If the Map's inner type is anything else (not a tuple), the assertion `unreachable!("map inner type must be a tuple")` panics. In a healthy catalog every Map(K, V) is stored with a tuple inner type, so this indicates a malformed map type reached the resolver.

Solutions

  1. Inspect the type with SELECT typeof(col); ensure it is exactly MAP(key_type, value_type)
  2. Re-cast explicitly: CAST(col AS MAP(VARCHAR, VARIANT)) before subscripting
  3. Fix the source of the malformed type (UDF return type, import pipeline, table schema) rather than casting around it
  4. If a valid Map triggers the panic, report a planner bug with the schema

Example fix

// before: access on a map with malformed inner type
SELECT m['k'] FROM t;
// after: normalize the type first
SELECT CAST(m AS MAP(VARCHAR, VARIANT))['k'] FROM t;
Defensive patterns

Strategy: type-guard

Validate before calling

SELECT typeof(m) FROM t LIMIT 1; -- must be MAP(K, V); recast if the inner type is malformed

Type guard

fn is_proper_map(ty: &str) -> bool {
    let inner = ty.trim_start_matches("MAP(").trim_end_matches(')');
    ty.starts_with("MAP(") && inner.split(',').count() == 2
}

Prevention

When it happens

Trigger: Performing key-based access on a value typed as Map whose inner type is not Tuple(String, T) — e.g. after a bad CAST to MAP(...) with a non-tuple inner type, schema metadata drift, or map-typed values produced by UDFs/functions with unconventional types.

Common situations: Tables created by other engines/import pipelines whose Map type metadata doesn't match Databend's internal Map(K,V) tuple representation; UDFs returning improperly shaped map types; older dumps restored into newer versions.

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/2c1e2a28b20e546e. Report an issue: GitHub.

Appendix: source

Thrown at src/query/sql/src/planner/semantic/type_check/variant.rs:541

                    ScalarExpr::passthrough_nullable_type(DataType::from(&table_data_type), [
                        &scalar,
                    ]);
                scalar = FunctionCall {
                    span: expr_span,
                    func_name: "get".to_string(),
                    params: vec![Scalar::Number(NumberScalar::Int64((idx + 1) as i64))],
                    arguments: vec![scalar.clone()],
                    return_type: Box::new(return_type),
                }
                .into();
                continue;
            }
            let box (path_scalar, _) = self.resolve_literal(span, &path_lit)?;
            table_data_type = match table_data_type {
                TableDataType::Array(inner_type) => *inner_type,
                TableDataType::Map(inner_type) => match inner_type.remove_nullable() {
                    TableDataType::Tuple { fields_type, .. } => fields_type[1].clone(),
                    _ => unreachable!("map inner type must be a tuple"),
                },
                TableDataType::EmptyArray | TableDataType::EmptyMap => TableDataType::Null,
                data_type => data_type,
            };
            table_data_type = table_data_type.wrap_nullable();
            scalar = FunctionCall {
                span: path_scalar.span(),
                func_name: "get".to_string(),
                params: vec![],
                arguments: vec![scalar.clone(), path_scalar],
                return_type: Box::new(DataType::from(&table_data_type)),
            }
            .into();
        }
        let return_type = scalar.data_type().into_owned();
        Ok(Box::new((scalar, return_type)))
    }

View on GitHub (pinned to 288d84d76e)