Class 1: The GraphQL Error Model
Duration: 20 minutes | Difficulty: Beginner to Intermediate | Prerequisites: You can read a GraphQL schema and have executed queries against some GraphQL API. No server or client framework knowledge is required for this class.
What You'll Learn
By the end of this class, you will:
- Know the three kinds of response the specification defines, and which of them carry errors
- Distinguish a request error from an execution error, and predict what each does to the
dataentry - Read every key of an error (
message,locations,path,extensions) and know which are required - Explain how the specification's execution rules produce a partial result when a field fails
- Know exactly what the specification does not define, which is the gap the rest of this course fills
One failing field, one honest response
We will start with the response, because everything else in this course is a decision about what goes into it. Here is a query against the Movie Database schema used throughout this site's tutorials, asking for a movie, its rating, and its directors:
query MovieWithDirectors {
movieById(id: "1") {
title
rating
directors {
name
}
}
}
Suppose the ratings backend is down, and the resolver for rating raises an exception. GraphQL does not fail the whole request; it keeps executing the other fields, and the response looks like this:
{
"errors": [
{
"message": "Rating service unavailable",
"locations": [{ "line": 4, "column": 5 }],
"path": ["movieById", "rating"]
}
],
"data": {
"movieById": {
"title": "The Shawshank Redemption",
"rating": null,
"directors": [{ "name": "Frank Darabont" }]
}
}
}
Both entries are present at once. The title and directors fields resolved normally, rating is null, and the errors list explains which position failed and why. A response that carries data and errors together is not a malformed response or a server bug. The specification's Response section opens by saying exactly this:
A response may contain both a partial response as well as a list of errors in the case that any execution error was raised and replaced with null.
This is the single most important fact about GraphQL error handling, and most of the friction we will meet in later lessons comes from tools and habits that assume a request either succeeds or fails as a whole. GraphQL deliberately does not make that assumption, which is what HTTP status codes in Class 3, client caches in Class 7 and nullability in Classes 2 and 8 all run into.
The three kinds of response
The specification defines a response as one of three things. The definition reads the same in the ratified September 2025 edition and in the current working draft, and this class quotes the ratified edition throughout:
An execution result is what you get when a query or mutation actually executed. It must contain a data entry. If any errors were raised during execution, it must also contain an errors entry, and that entry "must be a non-empty list"; if execution completed without raising any errors, the errors entry "must not be present". An empty errors: [] is therefore never valid: either there are errors and the list has at least one, or the key is absent.
A response stream is what a subscription returns: a stream in which each event is itself an execution result. Errors inside a subscription therefore follow the same rules as any execution result, an important detail we return to in Class 6 when wiring up subscription error handling on the server.
A request error result is what you get when the request failed before execution started. It must contain an errors entry with at least one error, and it "must not contain an entry with key data". The specification means the key is absent entirely, not present with a null value. This distinction between an absent data and a null data matters, and the next section explains it.
The working draft, dated 17 September 2026, has reworded parts of this section since the September 2025 edition without changing the rules. It says "operation execution" where the edition says "execution", it restates the definition of a response position, and it adds that a request error can also come from a failure to establish the source event stream of a subscription. The quotes in this class come from the ratified edition, so they may differ in small ways from the draft you read online.
Request errors vs execution errors
The specification sorts every error a GraphQL service can produce into two categories, and the Errors section defines both.
The specification defines a request error in one sentence:
A request error is an error raised during a request which results in no response data.
Request errors are typically raised before execution begins, for one of these reasons:
- The document failed to parse.
- Validation rejected the document.
- The service could not determine which operation to run.
- A variable's value could not be converted to the type the operation declares for it.
When a request error is raised, execution never starts, the response is a request error result, and no data entry appears. The specification adds a sentence worth remembering, because the error taxonomy in Class 4 builds on it:
A request error is typically the fault of the requesting client.
The specification defines an execution error the same way:
An execution error is an error raised during the execution of a particular field which results in partial response data.
Common causes include:
- The resolver raised an exception.
- An argument value could not be converted to the type the schema declares for it.
- The value the resolver returned could not be converted to the field's type.
- The resolver returned
nullfor a field whose type isNon-Null.
The specification calls the checking and converting of values coercion, in its rules for arguments and for results. Passing "abc" for an Int argument fails it, and so does a resolver that returns "abc" for an Int field. Execution continues around the failure: the errored position becomes null, siblings keep executing, and the result is partial data plus an errors entry. Mirroring its sentence about request errors, the specification notes:
An execution error is typically the fault of a GraphQL service.
One naming note so that older material does not confuse you: the September 2025 edition renamed this second category. A note in the Execution Errors section says so:
In previous versions of this specification execution error was called field error.
The rename came from PR #1152. The October 2021 edition, which the September 2025 edition superseded, still says "field error" throughout. Most framework documentation and blog posts you will encounter say "field error" too. This course uses the ratified term, execution error, and notes the legacy name where a cited source uses it.
The practical decision table falls out directly. If data is absent, the request never executed: fix the request. If data is present, the request executed, and any errors alongside it are per-position failures inside an otherwise valid result. Class 3 will show that the GraphQL-over-HTTP specification builds its entire status-code scheme on exactly this split.
Anatomy of an error
Each error in the errors list is a map, and the Error Result Format section defines its keys precisely.
Here is the error from the response at the start of this class, with an extensions entry added, so that all four keys appear in one place:
{
"message": "Rating service unavailable",
"locations": [{ "line": 4, "column": 5 }],
"path": ["movieById", "rating"],
"extensions": {
"code": "UNAVAILABLE",
"retryAfterSeconds": 30
}
}
The subsections below take the keys one at a time.
message
message is the only required key, and the Error Result Format section requires it in one sentence:
Every error must contain an entry with the key "message" with a string description of the error intended for the developer as a guide to understand and correct the error.
The audience the specification names is the developer, and the specification leaves open whether a message is suitable to show an end user. graphql.org's security guidance gives a reason for caution:
In addition to request errors, details about errors that are raised during field execution should be masked as they may reveal sensitive information about the server or underlying data sources.
Machine-readable classification belongs in extensions (Class 4). A client that shows end users a message can choose its text from that classification, which is what Class 7 does.
locations
locations points into the document, meaning the operation text the client sent. The specification describes it in one sentence:
If an error can be associated to a particular point in the requested GraphQL document, it should contain an entry with the key "locations" with a list of locations, where each location is a map with the keys "line" and "column", both positive numbers starting from 1 which describe the beginning of an associated syntax element.
In plain terms, each location gives a line number and a column number, both counted from 1, that mark where the related part of the document begins.
Here is the query from the start of this class again, with line numbers:
query MovieWithDirectors {
movieById(id: "1") {
title
rating
directors {
name
}
}
}
The rating field sits on line 4. The line starts with four spaces of indentation, so the r of rating is the fifth character, in column 5. That gives the location in the error, { "line": 4, "column": 5 }, and it is what graphql-js, the JavaScript reference implementation, returns for this failure. The value is a list because some errors involve more than one place, such as two fields that conflict with each other.
Locations matter most for request errors. A request error "results in no response data", so no path can point into the result, and the location is the only pointer the client receives. If the client misspells rating as ratting, graphql-js rejects the document during validation and returns this request error result:
{
"errors": [
{
"message": "Cannot query field \"ratting\" on type \"Movie\". Did you mean \"rating\"?",
"locations": [{ "line": 4, "column": 5 }]
}
]
}
The location points at the misspelled field on line 4. The error has only message and locations, and the response has only errors, because the request failed before execution started.
path
path points into the result. If the error is associated with a field in the result, the error must carry a path: a list of segments from the root of the response down to the response position that raised the error. Each segment is either a response name, which is a string, or a list index, which is an integer counted from 0. So ["movieById", "directors", 0, "name"] means "the name of the first director of that movie". Two details matter here, and the first is about aliases. The section that defines a response position covers them:
If a path segment is associated with an aliased field it must use the aliased name, since it represents a path in the response, not in the request.
A response name is therefore the field's alias when it has one, and the field's name otherwise. The second detail is the purpose of path, which the specification states outright:
This allows clients to identify whether a null resolved result is a true value or the result of an execution error.
That sentence matters for two later classes: Class 2 is entirely about what happens when correlating path against data is the only way to know what a null means, and Class 8 is about the proposal to fix it.
One more rule about path concerns propagation, and it is worth quoting now, before Class 2 explores it fully. It sits in the same Error Result Format section:
If the field which experienced an error was declared as Non-Null, the null result will bubble up to the next nullable field. In that case, the path for the error should include the full path to the result field where the error was raised, even if that field is not present in the response.
The path therefore names the field where the error was raised, even when propagation has removed that field from the response.
extensions
extensions is the extension point, and the Error Result Format section defines it in three sentences:
GraphQL services may provide an additional entry to errors with key
extensions. This entry, if set, must have a map as its value. This entry is reserved for implementers to add additional information to errors however they see fit, and there are no additional restrictions on its contents.
This is where error codes, retry hints, request IDs, and everything else machine-readable will live in this course. Just as important is the rule that follows:
GraphQL services should not provide any additional entries to the error format since they could conflict with additional entries that may be added in future versions of this specification.
The specification even shows a counter-example with code and timestamp placed directly on the error, and marks it discouraged. Apollo Server, Spring for GraphQL and Netflix DGS all follow this rule, and put their custom data under extensions.
Partial results are the contract
Look once more at the example response above. GraphQL delivered two of the three requested fields and an exact account of the third's failure. The execution rules require that behavior:
If an execution error is raised while resolving a field (either directly or nested inside any lists), it is handled as though the response position at which the error occurred resolved to null, and the error must be added to the "errors" list in the execution result.
Nested execution under the failed position halts, and sibling execution continues.
For API design this is a genuinely different contract from a typical REST endpoint, where one failing join usually costs you the entire 500 response. A GraphQL screen that aggregates ten backends can render nine sections and show an error message for the tenth. The contract also has costs, in three places:
- Clients. Every client must be written knowing that any nullable position might be
nullfor two different reasons. Class 2 explains the two reasons. - Caches. Every cache must decide what to store when data arrives accompanied by errors. Class 7 covers the choices.
- Monitoring. Every monitoring dashboard must decide what "success" means when a response is 90% data and 10% errors. Class 3 and Class 4 cover the signals a dashboard can use.
Much of the rest of this course covers the answers the community has developed to these three questions.
What the specification deliberately does not define
The Response section is short, and three questions it leaves open shape everything that follows.
There is no error vocabulary. The specification defines the shape of an error but not one single standard code, category, or classification. extensions exists precisely so implementers can build their own, and there are "no additional restrictions on its contents". Class 4 builds an error taxonomy and surveys the conventions that Apollo Server and Netflix DGS ship; Classes 9 and 10 show what Shopify, GitHub, and GitLab chose.
There is no transport mapping. Section 7 of the specification does not mention HTTP status codes, and GraphQL "does not require a specific serialization format", let alone a specific transport. The mapping of responses onto HTTP lives in a separate specification, GraphQL-over-HTTP, which is Class 3's subject, including why the common claim that GraphQL always returns 200 is no longer accurate.
There is no opinion on business errors. Whether "review text too long" should be an execution error in the errors list or a typed value in your schema is a schema-design question the specification leaves entirely to you. That decision line, expected outcomes in the schema versus failures in the errors list, is drawn carefully in Class 5 and defended for the rest of the course.
The specification gives every team the same response structure and leaves these three questions to implementers, which is why error handling looks so different from one GraphQL API to the next. The remaining classes cover each question and the answers the community has settled on.
Summary
The specification defines three kinds of response:
- An execution result always has
data, and carries a non-emptyerrorslist only if errors were raised. - A response stream is a stream of execution results, used for subscriptions.
- A request error result has
errorsand must not havedataat all.
It sorts errors into two kinds:
- Request errors mean the request never executed, and they are typically the client's fault.
- Execution errors mean a specific field failed during execution, and they are typically the service's fault. The September 2025 edition renamed them from the older term "field error".
An error can carry four keys:
message, the only required key, intended, in the specification's words, "for the developer".locations, which points into the document.path, which points into the result.extensions, the only sanctioned place for custom, machine-readable data.
Partial results are guaranteed behavior: errored positions become null, siblings continue, and clients correlate errors[].path against data to tell whether a null is a true value or the result of an execution error. The specification deliberately leaves error codes, HTTP mapping, and the placement of business errors undefined, which is the gap the remaining eleven classes fill.
Further Reading
- GraphQL Specification (draft), Response: the primary source for this entire class; the Errors subsection is short enough to read in ten minutes and worth reading in full.
- GraphQL Specification (draft), Handling Execution Errors: the execution-side rules for how a raised error becomes a
nulland anerrorsentry; Class 2 walks through its propagation rules in detail. - GraphQL Specification (September 2025), Errors: the current ratified edition, and the first to use the term "execution error"; cite it whenever you need a released document instead of the living draft.
- GraphQL Specification (October 2021), Errors: the previous edition, superseded in September 2025, and the one most framework documentation was written against; useful for tracing where the "field error" wording in older sources comes from.
- GraphQL spec repository: where the draft evolves; the pull requests and RFC discussions are the best record of why the response rules are worded the way they are.
- graphql.org, Introduction to GraphQL: the official learning material; its sections on execution and serving over HTTP set useful context for Classes 2 and 3.
What's Next
Class 2 follows a single execution error through a schema of Non-Null types and shows how the propagation rules remove data that resolved successfully. That propagation behavior, and the ambiguous null it leaves behind, is the specification's most consequential error-handling decision and the reason the nullability working group was formed.