A GraphQL response returns data as null with one error — how do you find the failing field?
answer
- The null is nowhere near the cause
- The path is the only real evidence
- Ancestors that were blanked stay silent
- Walk the chain from the root down
- One error can hide a whole lost response
basics
~20 sRead 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.
solid answer
~40 s`data: null` with one error means a field error propagated through an unbroken chain of Non-Null fields all the way to the root. The evidence is the error's **`path`**, which addresses the field where the error originated — ancestors included, list positions as integers — and is not rewritten as the error climbs. Walk that path in the schema starting at the root and check each field's nullability; the first nullable one would have absorbed the failure, and finding none explains the null `data`. The nulled ancestors add no entries of their own, so the errors list never reveals the blast radius, and the count of errors never measures how much was lost. Then ask what changed on that path — a new failure rate, or a nullability edit that re-routed an old failure.
code
json · 10 lines{
"data": null,
"errors": [
{
"message": "Upstream asset service exceeded its deadline",
"path": ["event", "venue", "seatingChartUrl"],
"extensions": { "code": "UPSTREAM_TIMEOUT" }
}
]
}go deeper
Be ready to say the error's path names the field that failed, and that the null you see in the response is usually somewhere above it rather than at the cause.
Explain how to walk the path against the schema and find the first nullable field, and why an unbroken Non-Null chain from the root is what turns one field error into a null data entry.
Demonstrate the whole diagnosis under time pressure: classify the failure, read the path, reconstruct the chain, correlate with what deployed, and reproduce with a narrowed selection rather than guessing.
Own the systemic view: treat nullability edits as blast-radius changes that belong in review and in change history, and make sure the operation-level traces exist, because the response body deletes the evidence it destroyed.
## The shape of the evidence A response body with `"data": null` and a single entry in `errors` is not "the request failed". It is a very specific outcome: **one field error was raised somewhere in the tree, and every field on the path from the root down to it was Non-Null**, so propagation ran out of nullable ancestors and consumed the entire response. Everything the operation asked for is gone, including root fields that had nothing to do with the failure. The single most useful thing to know here is where the evidence lives, because two of the obvious places have none: * The nulled ancestors leave **no** entries of their own. At most one error is added per field, and propagation adds nothing, so the blast radius is invisible in the errors list. * The count of errors tells you nothing about how much data was lost — one entry can accompany a wholly destroyed response. What does carry the information is the error's **`path`**: it names the field where the error originated, ancestors included and list positions as 0-indexed integers. It is not rewritten as the error climbs, so it still points at the real culprit rather than at the ancestor that ended up null. ## The diagnosis, step by step 1. **Classify the failure first.** `data` present and null with a path-carrying error is a field error raised during execution. That is a different animal from a document rejected before execution ever started, and the two have entirely different causes. 2. **Read the path as an address.** `["event", "venue", "seatingChartUrl"]` is the field that actually failed. Do not start from where the null appears — after propagation, the null is nowhere near the cause. 3. **Walk that path in the schema, root first.** At each step ask whether the field's type is nullable. The first nullable field you meet is where propagation would have stopped. If you get all the way down without meeting one, you have explained `"data": null` completely. 4. **Ask what changed on that path.** A resolver that has always been able to fail, plus a chain that has always been Non-Null, would have been blanking responses all along. When the symptom is new, either the failure rate changed or the chain did. 5. **Reproduce narrowly.** Send an operation that selects only the suspect field and its ancestors. If it reproduces, the failure is genuinely at that field; if it does not, look for a load- or data-dependent trigger rather than a broken resolver. ## A worked incident on a ticketing graph A ticketing service starts returning blank event pages. The client logs show a body with `"data": null`, one error, and the path `["event", "venue", "seatingChartUrl"]`. Nothing else. Walking the schema from the root: `Query.event` returns `Event!`, `Event.venue` returns `Venue!`, `Venue.seatingChartUrl` returns `String!`. Three Non-Null links, no absorber anywhere, so a single failing image URL nulls the entire response. The change that morning was a deploy that tightened `seatingChartUrl` from `String` to `String!`. No resolver changed. The asset service behind it had always been the slowest dependency in the operation, occasionally exceeding the service's 340 ms p99 budget and returning nothing — and until that deploy, "returning nothing" meant a missing image on an otherwise complete page. Afterwards the same timeout means an empty page. Two things this incident teaches that read well in an interview: * **The blast radius changed without any resolver changing.** A nullability edit is not cosmetic; it re-routes every future failure on that path. * **The work was still done and paid for.** `title`, `doorsOpenAt` and the whole seat map resolved successfully, concurrently, inside budget. Their results were discarded because their container could not survive. The service spent its full latency budget to return `null`. ## Where to look when the response is not enough Server-side, the operation's own trace is the map the response refuses to give you: it shows which field resolvers ran, which one raised, and how far the failure travelled. Failing that, the pattern is recognisable from aggregate signals — a spike in responses whose `data` is null, correlated with error rate on exactly one downstream dependency, and no correlated change in any resolver's own duration except that one. Two traps worth naming. A `null` in `data` with **no** error entry is not a failure at all; it is a legitimately nullable field that resolved to null, and chasing it as an incident wastes an hour. And an error whose path points deep into a list is describing one entry, not the collection — the index in the path is the difference between one bad row and a systemic fault. ## What you are allowed to conclude From one response you can state exactly two things with certainty: which field raised the error, and that every field above it on that path was Non-Null. Everything else — how many other fields succeeded, how long they took, whether other elements would have failed too — is unrecoverable from the body alone, because propagation deleted the evidence along with the data.
- The path points at a field nobody changed. What do you look at next?Two things changed if the symptom is new: the failure rate of that field's dependency, or the nullability of something on the path above it. Check the schema's history on that chain first, because a nullability edit re-routes every future failure without touching a resolver, and a dependency that has always failed occasionally will start blanking whole responses the day the chain above it becomes Non-Null.
- How do you tell this apart from a null that is simply a legitimate value?A legitimate null has no error entry pointing at it. Fields that are nullable and resolved to null produce no entry in `errors` at all, so a response containing nulls and an empty-of-relevance errors list is working as designed. Only a path-carrying error identifies a real failure, and only its path identifies which field it was.
- Why can the errors list not tell you how much data was lost?Because propagation is silent. At most one error is added per field, and the ancestors nulled on the way up add nothing, so a single entry can accompany an entirely destroyed response. The only way to measure the damage is to read the path against the schema and work out where the null actually landed — or to look at the server-side trace, which still records the resolvers that ran and succeeded.
saying these in an interview costs you the question
- Reads the null in data as the location of the fault
- Counts errors entries to gauge the damage
- Assumes data null means the document was rejected
- Treats every null in a response as a failure
- Thinks the path is rewritten as the error climbs
- Blames a resolver without checking the chain's nullability