skip to content

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%

answer

  1. Enums coerce in two directions
  2. Direction decides the class
  3. Client sends it versus server returns it
  4. Before execution versus during it
  5. Whole operation dead versus one null

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.

solid answer

~50 s

An enum is coerced in two directions, and the direction fixes the error class. **Input coercion** happens before execution: a literal the enum does not declare is rejected by validation, and a `variables` value the enum does not declare is rejected while variable values are coerced — both before the first resolver runs, so both are request errors and the response has no `data` entry. **Result coercion** happens during execution: if a resolver returns a value that is not one of the enum's declared members, the executor raises a field error, that field resolves to `null`, an errors entry is appended, and sibling fields still resolve. So the same missing enum member produces a total failure when a client sends it and a single hole when a service returns it. The same split applies to custom scalars, which also define separate input and result coercion.

code

graphql · 8 lines
graphql
enum ClaimStatus { FILED ASSESSING APPROVED DENIED CLOSED }

type Claim { reference: String!, status: ClaimStatus }
type Query { claims(status: ClaimStatus!, first: Int! = 25): [Claim!]! }

query OpenClaims($status: ClaimStatus!) {
  claims(status: $status, first: 14) { reference status }
}

go deeper

for a junior

Know that an enum's legal values are fixed by the schema and that sending an undefined one fails the whole request rather than one field. Recognising the two directions by name is enough at this level.

for a middle

Explain both coercion directions and where each sits relative to execution: literals fail validation, variable values fail coercion, and both are pre-execution, while a resolver's undefined return value fails during value completion as a field error.

for a senior

Show you can diagnose the two symptoms apart in production — a total failure on one operation versus a small null rate on one field path — and connect both to release skew between the service producing values and the schema declaring them.

for a principal

Own the rollout discipline: enum members are a shared contract between producers, the schema and every client, so decide deliberately whether unknown producer values null a field, map to a catch-all, or block the release.

## An enum is coerced twice, in opposite directions An enum type in a GraphQL schema is used in two places, and the specification gives it a separate coercion rule for each: - **Input coercion** — a value arriving from the client, as a literal in the document or as an entry in the `variables` map, is coerced *into* an enum value before execution. - **Result coercion** — a value a resolver returned is coerced *out* to one of the enum's declared values as the executor completes the field. Both can fail on exactly the same symptom: a value that is not a member of the enum. But they fail at different moments in the request's life, and the moment is what fixes the error class. Input coercion runs before execution, so its failure is a **request error**. Result coercion runs during execution, so its failure is a **field error**. ## The input direction: nothing runs Take an insurance claims graph whose schema declares: ```graphql enum ClaimStatus { FILED ASSESSING APPROVED DENIED CLOSED } type Query { claims(status: ClaimStatus!, first: Int! = 25): [Claim!]! } ``` The claims-intake team introduces a new internal state, `SUBROGATION_PENDING`, and a client build starts sending it. Two shapes, one outcome: - As a **variable** — `{"status": "SUBROGATION_PENDING"}` against `$status: ClaimStatus!` — coercing variable values happens after validation and before the first resolver, and the value is not a member of the enum. A request error is raised. Nothing executes; the response carries no `data` entry. - As a **literal** in the document — `claims(status: SUBROGATION_PENDING)` — the document is now checked against the schema during validation, and an enum literal that is not a member of the type is not a valid value for that input position. Validation fails, which is also a request error. So the two shapes take different routes and land in the same place: pre-execution, whole operation dead, no partial result. That symmetry is the point of the question. A candidate who says "the literal fails validation and the variable fails at runtime" has the second half wrong in the way that matters — coercion is still before execution. ## The output direction: one hole Now flip it. The schema is unchanged, but a service behind the graph starts returning the string `SUBROGATION_PENDING` for a claim's `status` field. The resolver returned successfully; the executor is the one that objects, because the value is not one of the enum's declared members. That is a field error: the field resolves to `null` and an entry is appended to the errors list, while every sibling field on the same claim resolves normally. If `status` were declared nullable, a client reading a page of 14 claims gets 14 claims back, some with `status: null`, and can render the rest of each row. The declared nullability of that position decides how much of the response survives — a separate subject, and a very consequential one. ## Why this bites in a distributed graph In an 11-service graph the producer of a value and the schema that declares it are usually owned by different teams on different release trains. A new enum member is added to a source system on a Tuesday; the schema that declares `ClaimStatus` is updated whenever that team's next change ships. In between, the graph is in skew, and the skew surfaces as field errors on every object that happens to carry the new value — quietly, one field at a time, with a healthy overall response body. Meanwhile a client that ships the new value as an input hits the other class: a hard, total request error on its very first call. The diagnostic tell is worth memorising. The same missing enum member produces: - **input side** → no `data` entry, every affected caller sees a blank result, error rate for that operation goes to 100%; - **output side** → a normal-looking `data` map, a small non-zero errors rate, and nulls concentrated on one field path. Two dashboards, one root cause. ## Same rule, other types The split is not special to enums; enums are just the clearest case because they have a small closed set of legal values. The same shape applies to custom scalars: a `variables` value that a scalar's input coercion rejects — a malformed policy number for a custom `PolicyNumber` scalar — is a request error, while a resolver returning a value that the same scalar's result coercion rejects is a field error. It also applies to structural mismatches: a missing required key in an input object arrives as a request error, while an object-typed field whose resolver returns something unusable is a field error. ## What is specified and what is not Specified: that enums have separate input and result coercion, that a failure to coerce a variable value raises a request error, that a result that cannot be coerced raises a field error, and that a field error nulls its field. Not specified: what a server should do *instead* of failing — mapping unknown producer values onto a catch-all member, filtering affected rows out of the list, or logging and returning null deliberately are all engineering choices. Nor does the specification say whether adding a member to an enum is a safe change for clients; that is schema-evolution judgement, not an execution rule.

  • Does the same input-versus-output split apply to custom scalars?
    Yes. A custom scalar defines both input coercion and result coercion. A `variables` value its input coercion rejects fails before execution and is a request error; a resolver return value its result coercion rejects fails during execution and is a field error that nulls the field. Enums are just the crispest example because their legal set is small, closed and visible in the schema.
  • An undefined enum value appears in a variable definition's default value in the document. Which class is that?
    A request error. A default value written in the document is a literal in an input position, so it is checked against the schema during validation, before execution begins — the same route an inline literal argument takes. Nothing runs, and the response has no data entry, even though no client ever supplied the value at call time.

saying these in an interview costs you the question

  • Says an undefined enum value is always a field error
  • Thinks variable coercion happens inside the resolver
  • Claims the server coerces unknown values to a default member
  • Expects the raw unknown string to appear in data
  • Treats input and output coercion as the same rule

context