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
- Inspect the type with SELECT typeof(col); ensure it is exactly MAP(key_type, value_type)
- Re-cast explicitly: CAST(col AS MAP(VARCHAR, VARIANT)) before subscripting
- Fix the source of the malformed type (UDF return type, import pipeline, table schema) rather than casting around it
- 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
- Always declare Map columns as MAP(key_type, value_type) at table creation
- Recast values from import pipelines or UDFs to canonical MAP types before subscripting
- Validate restored/dumped tables with typeof() checks before querying
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
- internal error: entered unreachable code
- internal error: entered unreachable code
- rename_database: src (db) should exist
- internal error: expect TxnGetResponseGet of get database…
- internal error: expect some TxnGetResponseGet, but got
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)