CONCEPT Cited by 1 source
GraphQL error propagation¶
Definition¶
GraphQL error propagation is the runtime semantic
defined by the GraphQL specification: when a resolver throws
an error on a non-nullable field, the error "bubbles up"
the response tree until it reaches the nearest nullable
ancestor, where it is replaced by null. The error is
appended to response.errors; the data tree surfaces only
as much of the partial result as is still valid.
This behaviour is a feature — partial success is first-class in GraphQL — but it interacts subtly with how errors are modelled.
The subtle interaction¶
Zalando's 2021 error-modeling post names this interaction as one of three axes in its classification framework:
"We also have to remember not to disrupt GraphQL semantics of error propagation. If an error occurs in one place in the query, it propagates upwards in the tree till the first nullable field occurs. This propagation does not happen with error types in Schema. It is essential to model these schema error types for only specific use-cases."
(Source: sources/2021-04-12-zalando-modeling-errors-in-graphql)
In other words:
- An Error (in
response.errors) propagates. - A Problem (a schema-modelled union branch) does not propagate — it's just which arm of the union the server returned.
Why this matters for design¶
Consider a product page that queries product(id: $id).
- If we return the error as an
Errorinresponse.errorsandProductis non-null, the field propagates to null, and the nearest nullable ancestor (say, the query root) sees null. The UI has exactly one place to check: "isdata.productnull?" - If we return the error as a
ProductNotFoundProbleminunion ProductResult = Product | ProductNotFoundProblem, every place in the schema that references aProductResult— potentially many — must spread the union:
product(id: $id) {
... on Product { name price }
... on ProductNotFoundProblem { message }
}
collection(id: $bar) {
... on Collection {
products {
... on Product { name }
# ... on ProductNotFoundProblem { } ← required here too
}
}
}
This is the "hammer and nails" failure mode the post warns against. Using Problem types at every level destroys the UX of GraphQL.
The design rule it implies¶
Schema-level Problem types should appear only where the
caller wants to branch explicitly — canonically at the
mutation boundary, where a RegisterResult branching on
RegisterSuccess | RegisterProblem maps directly to
"submit succeeded" vs "show validation errors to user". At
query leaves that aren't customer-actionable, use Errors
and let propagation handle it.
Worked counter-example from the post¶
{
product(id: "foo") {
... on ProductSuccess { success }
... on ProductError { error }
}
collection(id: "bar") {
... on CollectionSuccess {
products {
... on ProductSuccess { success }
}
}
... on CollectionError { error }
}
}
The post calls this "too much to type in a query and too many branches to handle in the code" — and this is a direct consequence of abandoning GraphQL's error-propagation semantics in favour of schema types for non-Customer-actionable failures.
Seen in¶
- sources/2021-04-12-zalando-modeling-errors-in-graphql — where the semantic is made explicit as a design constraint.