prestodb/presto · error · PrestoException
PLAN_SERIALIZATION_ERROR
PLAN_SERIALIZATION_ERROR
Error message
Cannot serialize plan to JSON
What it means
CanonicalPlanGenerator.writeValueAsString serializes objects (canonical plans/subplans) with Jackson during canonical planning. On JsonProcessingException it throws PLAN_SERIALIZATION_ERROR. Same family as CanonicalPlan serialization but triggered from the generator's internal write path, meaning a generated canonical structure could not be converted to JSON.
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/planner/CanonicalPlanGenerator.java:1224
private boolean shouldMergeJoinNodes(JoinType type)
{
return type.equals(JoinType.INNER);
}
private VariableReferenceExpression rename(VariableReferenceExpression variable, String nameHint, Context context)
{
VariableReferenceExpression newVariable = variableAllocator.newVariable(Optional.empty(), nameHint, variable.getType());
context.mapExpression(variable, newVariable);
return newVariable;
}
private String writeValueAsString(Object object)
{
try {
return objectMapper.writeValueAsString(object);
}
catch (JsonProcessingException e) {
throw new PrestoException(PLAN_SERIALIZATION_ERROR, "Cannot serialize plan to JSON", e);
}
}
private static EquiJoinClause canonicalize(EquiJoinClause criteria, Context context)
{
VariableReferenceExpression left = inlineAndCanonicalize(context.getExpressions(), criteria.getLeft());
VariableReferenceExpression right = inlineAndCanonicalize(context.getExpressions(), criteria.getRight());
return left.compareTo(right) > 0 ? new EquiJoinClause(left, right) : new EquiJoinClause(right, left);
}
private static Optional<EquiJoinClause> toEquiJoinClause(RowExpression expression)
{
if (!(expression instanceof CallExpression)) {
return Optional.empty();
}
CallExpression callExpression = (CallExpression) expression;
boolean isValid = callExpression.getDisplayName().equals(EQUAL.getFunctionName().getObjectName())
&& callExpression.getArguments().size() == 2View on GitHub (pinned to 55bb57d202)
Solutions
- Check the wrapped JsonProcessingException cause to identify the unserializable class
- Register missing Jackson serializers/modules for custom types via the plugin's JSON binding setup
- Align Presto versions across the cluster and retry; upgrade if the failing node type is a known fixed bug
Example fix
// plugin side
// before
public ObjectMapper getContext(Class<?> type) { return new ObjectMapper(); }
// after
public ObjectMapper getContext(Class<?> type) {
return jsonMapperBuilder().modules(new MyConnectorModule()).build();
} Defensive patterns
Strategy: try-catch
Validate before calling
// Verify custom types serialize before canonical planning runs
try { objectMapper.writeValueAsString(myConnectorTableHandle); } catch (JsonProcessingException e) { /* fix serializer registration */ } Try / catch
try { canonicalPlanGenerator.generate(plan); } catch (PrestoException e) { if (e.getErrorCode().equals(PLAN_SERIALIZATION_ERROR.toErrorCode())) { inspectCauseAndRegisterSerializer(e.getCause()); } else throw e; } Prevention
- Register Jackson serializers for every custom connector handle/expression type
- Avoid storing non-serializable objects (clients, lambdas) in plan metadata
- Test canonical planning in CI for custom connectors
- Use identical builds across the cluster
When it happens
Trigger: Canonical planning of a distributed plan (e.g. for EXPLAIN or worker exchange of canonical plans) where a node/field lacks a Jackson serializer or holds an unexpected runtime type.
Common situations: Custom connectors exposing non-serializable table handles; Presto version mismatches between coordinator and workers; plugins adding expressions/types without codec registration.
Related errors
- LANCE_ERROR
- PLAN_SERIALIZATION_ERROR
- {type_error} {className} {typeName} {fieldName}
- NOT_FOUND
- NOT_FOUND
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/3a81ab8f10f8288b.
Report an issue: GitHub.