prestodb/presto · error · SemanticException
TYPE_MISMATCH
TYPE_MISMATCH
Error message
Expression %s is not of type ROW
What it means
A dereference expression (base.field) requires the base expression to be of ROW type (after unwrapping TypeWithName and DistinctType). If the resolved base type is anything else, the analyzer throws TYPE_MISMATCH pointing at the base expression.
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java:589
}
if (outerScopeSymbolTypes.containsKey(NodeRef.of(node))) {
return setExpressionType(node, outerScopeSymbolTypes.get(NodeRef.of(node)));
}
throw missingAttributeException(node, qualifiedName);
}
}
Type baseType = process(node.getBase(), context);
addColumnSubfieldReferences(node, context);
if (((baseType instanceof TypeWithName) && ((TypeWithName) baseType).getType() instanceof RowType)) {
baseType = ((TypeWithName) baseType).getType();
}
if (baseType instanceof DistinctType) {
baseType = ((DistinctType) baseType).getBaseType();
}
if (!(baseType instanceof RowType)) {
throw new SemanticException(TYPE_MISMATCH, node.getBase(), "Expression %s is not of type ROW", node.getBase());
}
RowType rowType = (RowType) baseType;
String fieldName = node.getField().getValue();
Type rowFieldType = null;
for (RowType.Field rowField : rowType.getFields()) {
if (fieldName.equalsIgnoreCase(rowField.getName().orElse(null))) {
rowFieldType = rowField.getType();
break;
}
}
if (sqlFunctionProperties.isLegacyRowFieldOrdinalAccessEnabled() && rowFieldType == null) {
OptionalInt rowIndex = parseAnonymousRowFieldOrdinalAccess(fieldName, rowType.getFields());
if (rowIndex.isPresent()) {
rowFieldType = rowType.getFields().get(rowIndex.getAsInt()).getType();
}View on GitHub (pinned to 55bb57d202)
Solutions
- Verify the base expression's type is ROW; cast it if needed
- Use subscript syntax for maps/arrays instead of dot access
- Fix upstream schema/type so the column is actually a ROW
Example fix
// before SELECT my_map.key FROM t // after SELECT my_map['key'] FROM t
Defensive patterns
Strategy: type-guard
Validate before calling
Type baseType = expressionAnalyzer.getType(baseExpr);
if (!(unwrap(baseType) instanceof RowType)) {
throw new IllegalArgumentException("Base of field access must be ROW, got " + baseType);
} Type guard
boolean isRowAccess(Expression base, FunctionAndTypeManager types) {
Type t = types.getType(base);
while (t instanceof TypeWithName) t = ((TypeWithName) t).getType();
return t instanceof RowType;
} Try / catch
try {
return execute(sql);
} catch (SemanticException e) {
if (e.getCode() == TYPE_MISMATCH && e.getMessage().contains("is not of type ROW")) {
// suggest map subscript or cast, or report type of offending base
} else throw e;
} Prevention
- Use ['key'] subscripting for MAPs, dot access only for ROWs
- Check column types in metadata before generating dereference expressions
- Re-validate dereference expressions after schema changes
- Unwrap DistinctType when reasoning about field access in tooling
When it happens
Trigger: Using dot-notation field access on a non-ROW value, e.g. my_map.key where my_map is MAP, or col.field where col is VARCHAR/ARRAY.
Common situations: Confusing map access with row field access (SQL maps use [] not dot), dereferencing a column whose type changed after a schema change, accessing fields of a NULL-typed or unknown expression.
Related errors
- Expected column to be a time type but is
- INVALID_FUNCTION_ARGUMENT
- DUPLICATE_COLUMN_NAME
- INVALID_PARAMETER_USAGE
- MISMATCHED_SET_COLUMN_TYPES
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/0c5c9fc4fd059349.
Report an issue: GitHub.