skip to content

Errors & Null Propagation

A failed request still answers 200 with a partly-filled response, and the nullability in your schema decides how much of it survives. Interviewers ask because it inverts every REST habit a client has.

part ofGraphQLoverview, primer and where to startread it →
on this pageshow

questions

28

What entries can a single item in a GraphQL response's errors list carry, and which is required?

level: juniorimportance: must knowfreq 62%

answer

  1. One entry is mandatory, three optional
  2. Prose for humans, structure for programs
  3. One points at text, one at the response
  4. Locations count from one, list positions from zero
  5. extensions is the only sanctioned extra key

basics

~20 s

Only message is required, a string describing the failure. An error may also carry locations pointing into the document sent, path locating the field in the response, and extensions, a free-form map for server-specific data such as a code.

solid answer

~50 s

Each item in `errors` is a map with a small fixed vocabulary. **`message`** is the only unconditionally required entry: a human-readable string, with no structure the specification cares about. **`locations`** is optional — a list of `{ line, column }` maps, both counting from 1, marking where in the document the caller sent the relevant syntax element begins. **`path`** is required whenever the error can be tied to a particular field of the result: a list running from the root of `data` to that field, using response keys as strings and **0-indexed** integers for positions inside lists. **`extensions`** is optional, must be a map, and is the one slot the specification leaves entirely to the server — error codes, correlation ids, timing. The specification also asks services not to add any other top-level entry to an error, reserving that space for future editions, which is precisely why `extensions` exists.

code

json · 11 lines
json
{
  "data": { "warehouse": { "bins": [ { "stockItems": [ null ] } ] } },
  "errors": [
    {
      "message": "Forecast service timed out",
      "locations": [ { "line": 6, "column": 11 } ],
      "path": [ "warehouse", "bins", 4, "stockItems", 11, "reorderPoint" ],
      "extensions": { "code": "UPSTREAM_TIMEOUT", "traceId": "8f21c4" }
    }
  ]
}

go deeper

for a junior

Be able to name the four entries and say which one is mandatory. If you remember nothing else, remember that message is required prose and extensions is where anything server-specific lives.

for a middle

Explain what each entry points at: locations at the document text sent, path at a position in the response, extensions at whatever the server chose to attach. State the 1-based versus 0-based difference without hedging.

for a senior

Show you know which entries are safe to build behaviour on. Codes in extensions and paths are stable and machine-readable; message text is not, and treating it as an interface is how recovery logic silently stops running.

for a principal

Own the error format as a cross-team contract: one code vocabulary, a documented meaning per code, additive evolution only, and a shared mapping layer so every service in the graph emits the same shape.

## What this is about A GraphQL response is a map. When something went wrong, that map carries an `errors` entry holding a **list**, and each item in that list is itself a small map describing one failure. The response envelope — which top-level keys exist, when `data` is absent — is a separate concern. This is about the shape of **one item inside the list**, and its vocabulary is deliberately tiny: `message`, `locations`, `path`, `extensions`. ## message — the one entry that is always required Every error **must** carry `message`, a string describing the failure. The specification says nothing about its structure, its language, or its wording. It is prose written for a human being who is debugging, and that is the whole of its contract. The consequence matters more than the rule: because nothing constrains the text, nothing stops it changing. A server upgrade rewords it, a translation layer localises it, a hardening pass replaces it wholesale with something bland. Any program that branches on it is depending on an unversioned string. Log it, show it to a developer, put it in a bug report — never write `if (message.includes(...))`. ## locations — where in the document the caller wrote it Optional. When the error can be tied to a point in the document that was sent, the error **should** carry `locations`: a list of maps, each holding `line` and `column`, both **positive integers starting at 1**, marking the beginning of the associated syntax element. It is a list because a single error can implicate more than one place in the document. Note what it points at: the **text the client sent**, not the schema file and not the response. It answers "where did you write this?", never "where in the data did it break?". ## path — where in the response the failure landed When an error can be associated with a particular field of the result, the error **must** carry `path`. It is a list of segments running from the root of `data` down to that field. Segments that name fields are strings — the **response keys**, so an aliased field appears under its alias — and segments that name a position inside a list field are **0-indexed integers**. The asymmetry is a favourite interview trap: `locations` counts lines and columns from **1**; `path` counts list positions from **0**. They are different coordinate systems over different artefacts, and there is no reason for them to agree. `path` is what makes a partial response usable at all. Without it, a caller holding a body full of `null`s cannot tell a value that is legitimately absent from a hole left by a failure. ## extensions — the one sanctioned free slot A service **may** add an `extensions` entry. If present its value must be a map, and the specification then imposes nothing whatsoever on its contents. This is the designated home for everything the format does not model: a machine-readable code, a correlation id, a timestamp, a retry hint, timing data. Two things candidates routinely get wrong here. First, the `code` key inside `extensions` is a **widespread convention among server implementations, not a specified key** — the spec knows about `extensions` and nothing about what goes in it. Second, the per-error `extensions` map is not the response's own top-level `extensions` entry; they are different maps at different levels, and a client reading the wrong one finds nothing. ## Why you may not invent a fifth top-level entry The specification tells services **not** to add other entries directly to an error map, because that namespace is reserved for future editions of the specification to grow into. So a bespoke `severity` sitting beside `message` is out of contract; `extensions.severity` is entirely fine. That single rule is why `extensions` exists at all. ## Worked example A warehouse inventory graph. `StockItem` is one of those types that accreted for years — 37 fields, one of which, `reorderPoint`, is computed by a forecasting service that has just timed out for the item in list position 11: ```json { "data": { "warehouse": { "bins": [ { "stockItems": [ null ] } ] } }, "errors": [ { "message": "Forecast service timed out", "locations": [ { "line": 6, "column": 11 } ], "path": [ "warehouse", "bins", 4, "stockItems", 11, "reorderPoint" ], "extensions": { "code": "UPSTREAM_TIMEOUT", "traceId": "8f21c4" } } ] } ``` Read it entry by entry. `message` tells the on-call engineer what happened. `locations` says the offending field was written on line 6, column 11 of the document that was sent. `path` says the failure belongs to `reorderPoint` of the twelfth stock item (index 11) inside the fifth bin (index 4). `extensions.code` is the only part of the whole error a client should branch on, and it is there because this server chose to put it there. ## Why one error map, and why a list GraphQL is transport-independent, so an error cannot be an HTTP status; the response has to describe the failure itself. And execution does not stop at the first bad field — sibling fields keep resolving — so one response can legitimately carry several errors at once. A list of self-describing maps is the smallest structure that supports both facts.

  • A server wants to mark some errors as retryable. Where does that flag belong?
    Inside `extensions`, as a key of that map — for example `"extensions": { "code": "UPSTREAM_TIMEOUT", "retryable": true }`. It must not go beside `message` as a new top-level entry of the error: the specification reserves that namespace for future editions and tells services not to add to it. `extensions` exists exactly so servers can carry data the format does not model, and it has no constraints on its contents.
  • Is the code key inside extensions part of the GraphQL specification?
    No. The specification defines `extensions` and then says explicitly that it places no restrictions on its contents. A string `code` is a very widespread convention across server implementations and clients, which is why it feels standard, but nothing validates it, nothing reserves the key, and two servers may use entirely different vocabularies. Treat the code set as your own published contract, not as something the specification guarantees.
  • Why is message a poor thing for a client to branch on?
    Because nothing in the format constrains it. It is prose intended for a developer, so its wording can change in a server upgrade, be localised, or be replaced with something deliberately vague. A client matching on substrings is coupled to an unversioned string that no one will think to treat as a breaking change. Branch on a code in `extensions` and use `message` for logs and developer-facing display only.

saying these in an interview costs you the question

  • Claiming every error must include a path entry
  • Thinking code is a specified top-level key of an error
  • Adding custom keys beside message instead of inside extensions
  • Branching on the message string in client code
  • Saying locations points into the schema or the response
  • Assuming an errors entry means data must be absent

context

open as a page

In GraphQL, what does it mean to return an expected failure as data rather than as a top-level error?

level: juniorimportance: must knowfreq 54%

basics

~20 s

It means the schema declares the failure as its own type — a union member or an error field inside a payload — so the failure arrives under data as an ordinary selectable value instead of in the response's top-level errors list.

open as a page

What happens in GraphQL when a resolver for a Non-Null field raises an error?

level: juniorimportance: must knowfreq 72%

basics

~20 s

The field cannot hold null, so the error propagates to its parent. The parent becomes null if it is nullable; otherwise the error keeps climbing, and if every field up to the root is Non-Null, data itself becomes null.

open as a page

Why can a GraphQL mutation document leave a write half-applied?

level: juniorimportance: must knowfreq 61%

basics

~20 s

A document can carry several root mutation fields. They run one after another, and nothing wraps the group in a shared transaction, so if the second one fails the first one's write has already committed and stays committed.

open as a page

A GraphQL response contains both a data object and a non-empty errors list — what does that mean?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Execution ran and some fields failed while others resolved. The data object holds every field that succeeded, with null in the holes; each errors entry names the field that broke via its path. Both halves are real.

open as a page

In GraphQL, what is the difference between a request error and a field error?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A request error happens before execution — the document failed to parse or validate, or its variables would not coerce — so nothing ran and there is no data entry. A field error is raised while resolving one field, leaving data partial.

open as a page

What are the top-level keys of a GraphQL response body, and which of them may be absent?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Three, and no others: data, errors and extensions. The errors key appears only when something went wrong, and is then never empty. The data key is absent when the operation never executed. The extensions key is optional and free-form.

open as a page

How do you place nullability in a GraphQL schema so one failing field cannot blank the whole response?

level: middleimportance: must knowfreq 60%

basics

~20 s

Leave a nullable field on the path between every risky field and the root. A value that cannot be produced for a Non-Null position is absorbed by the nearest nullable ancestor, so that ancestor is the boundary of the damage.

open as a page

How does a GraphQL error's path entry locate the failed field, and what do its integers mean?

level: middleimportance: should knowfreq 50%

basics

~20 s

path lists segments running from the root of data down to the field: response keys as strings, and 0-indexed integers for positions inside list fields. Because it walks the response, an aliased field appears under its alias.

open as a page

How does a GraphQL client tell which member of a union result type it received?

level: middleimportance: should knowfreq 48%

basics

~20 s

By selecting the __typename meta-field, which returns the name of the concrete object type, and by writing one inline fragment per member for that member's own fields. A default branch handles members the document does not cover.

open as a page

In GraphQL, when does one failing list element null the entire list?

level: middleimportance: should knowfreq 58%

basics

~20 s

When the element type is Non-Null. A failing element cannot be represented as null in place, so the error propagates to the list, which becomes null and may keep climbing. With nullable elements the list survives with a null at that index.

open as a page

In a GraphQL response, how do you tell a legitimately null field from one nulled by an error?

level: middleimportance: should knowfreq 48%

basics

~20 s

Check the errors list. A null a resolver deliberately returned has no error pointing into it; a null caused by a failure has an errors entry whose path is that position or runs deeper through it.

open as a page

In GraphQL, when is an enum value outside the schema's definition a request error rather than a field error?

level: middleimportance: should knowfreq 41%

basics

~20 s

The direction of coercion decides. An enum value the schema does not declare is a request error when a client sends it, so nothing runs; it is a field error when a resolver returns it, nulling that field.

open as a page

In a GraphQL response, how does an absent data key differ from data present with the value null?

level: middleimportance: should knowfreq 54%

basics

~20 s

An absent data key means the operation never executed, so no resolver ran. A present data key whose value is null means execution did run and its result was discarded. Only the first guarantees nothing happened server-side.

open as a page

A slow dependency turns whole GraphQL responses null at peak — how do you reshape the schema to degrade partially instead?

level: seniorimportance: should knowfreq 47%

basics

~20 s

Find the unbroken Non-Null chain from the failing field to the root, decide the smallest unit consumers can render without that field, and make the link at that boundary nullable. Then bound the dependency with a timeout so it fails fast into that hole.

open as a page

How do you design the extensions payload of a GraphQL error so clients can branch on it safely?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Publish a small, stable, machine-readable code vocabulary inside extensions and have clients switch on that, never on the message string. Keep codes additive, require a default branch for unknown ones, and namespace anything beyond the code to avoid collisions.

open as a page

Which failures belong in a GraphQL result payload, and which belong in the top-level errors list?

level: seniorimportance: should knowfreq 47%

basics

~20 s

Expected outcomes of a valid request that a client renders differently belong in the payload as schema types. Faults — timeouts, bugs, unavailable dependencies — belong in the errors list, where operators and generic client handling can see them.

open as a page

A GraphQL response returns data as null with one error — how do you find the failing field?

level: seniorimportance: should knowfreq 47%

basics

~20 s

Read the error's path: it names the field that actually raised, not the ancestor that was nulled. Then walk that path in the schema from the root; if every field on it is Non-Null, propagation reached the root and nulled data.

open as a page

How do you make a GraphQL mutation safe for a client to retry?

level: seniorimportance: should knowfreq 54%

basics

~20 s

Have the client send a stable key it generates once per logical action and reuses on every retry. The server records that key with the outcome in the same transaction as the write, and on a repeat key returns the recorded result instead of writing again.

open as a page

A client blanks the page whenever a GraphQL response has any errors entry — what would you change?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Stop treating the errors list as a verdict on the whole response. Render everything data resolved and use each error's path to degrade only the region over that hole. Reserve a full failure screen for a body with no usable data.

open as a page

In a GraphQL API, why should request errors and field errors drive different retry and alerting rules?

level: seniorimportance: should knowfreq 44%

basics

~20 s

A request error is deterministic — the same document fails identically forever — so retrying is waste and the signal is a client or schema release. A field error is a transient dependency fault where a retry can succeed.

open as a page

How do you set a nullability policy for a large GraphQL schema owned by many teams?

level: principalimportance: should knowfreq 38%

basics

~20 s

Write a rule that derives ! from where the data comes from — Non-Null only for values produced with their parent, nullable across any boundary — then enforce it with schema lint in the build and check it against per-field error rates.

open as a page

In a large GraphQL schema, where is modelling errors as data worth its cost?

level: principalimportance: should knowfreq 38%

basics

~20 s

Only where a failure is a durable business outcome that clients render distinctly. Apply it through one house carrier and a small shared failure vocabulary, because a graph carrying three inconsistent styles is worse than either style applied everywhere.

open as a page

In a GraphQL API, how do you keep half-applied writes from becoming a standing source of corrupt data?

level: principalimportance: should knowfreq 41%

basics

~20 s

Make the field the atomic unit and enforce that at the boundary rather than in review: reject multi-write documents, require one idempotency convention across the graph, measure the rate of partially-applied mutation responses, and reconcile what genuinely cannot be atomic.

open as a page

For a GraphQL list whose elements each come from a call that can fail, which nullability wrapper do you choose?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Make the elements nullable and keep the list itself Non-Null, as in [BenefitElection]!. A failed element leaves one hole beside an error entry; the successful elements survive, because a Non-Null element would take the whole list with it.

open as a page

In a GraphQL error, what do the line and column values under locations point at?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

They point into the document text the client sent, marking the beginning of the relevant syntax element. Both numbers are positive integers starting at 1, and the entry is a list because one error can implicate several places.

open as a page

After one root mutation field errors, do the later root mutation fields still run?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

Yes. A field error produces null for that one field and an entry in the errors list, and the executor then moves on to the next root field and runs it. There is no early exit, so later side effects still happen.

open as a page

What is the top-level extensions key in a GraphQL response for, and what may a client assume about it?

level: seniorimportance: nice to knowfreq 26%

basics

~20 s

It is the one sanctioned place for anything a server sends beyond data and errors, because no other top-level entry is allowed. It must be a map when present, and a client may assume nothing about its contents.

open as a page