Skip to content

Rename field error to execution error; define response position #1152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 17, 2025
67 changes: 34 additions & 33 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@ A GraphQL schema may describe that a field represents a list of another type;
the `List` type is provided for this reason, and wraps another type.

Similarly, the `Non-Null` type wraps another type, and denotes that the
resulting value will never be {null} (and that a _field error_ cannot result in
a {null} value).
resulting value will never be {null} (and that an _execution error_ cannot
result in a {null} value).

These two types are referred to as "wrapping types"; non-wrapping types are
referred to as "named types". A wrapping type has an underlying named type,
Expand Down Expand Up @@ -461,14 +461,14 @@ more guidance.

A GraphQL service, when preparing a field of a given scalar type, must uphold
the contract the scalar type describes, either by coercing the value or
producing a _field error_ if a value cannot be coerced or if coercion may result
in data loss.
producing an _execution error_ if a value cannot be coerced or if coercion may
result in data loss.

A GraphQL service may decide to allow coercing different internal types to the
expected return type. For example when coercing a field of type {Int} a boolean
{true} value may produce {1} or a string value {"123"} may be parsed as base-10
{123}. However if internal type coercion cannot be reasonably performed without
losing information, then it must raise a _field error_.
losing information, then it must raise an _execution error_.

Since this coercion behavior is not observable to clients of the GraphQL
service, the precise rules of coercion are left to the implementation. The only
Expand Down Expand Up @@ -513,15 +513,15 @@ Fields returning the type {Int} expect to encounter 32-bit integer internal
values.

GraphQL services may coerce non-integer internal values to integers when
reasonable without losing information, otherwise they must raise a _field
reasonable without losing information, otherwise they must raise an _execution
error_. Examples of this may include returning `1` for the floating-point number
`1.0`, or returning `123` for the string `"123"`. In scenarios where coercion
may lose data, raising a field error is more appropriate. For example, a
floating-point number `1.2` should raise a field error instead of being
may lose data, raising an execution error is more appropriate. For example, a
floating-point number `1.2` should raise an execution error instead of being
Comment on lines +519 to +520
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
may lose data, raising an execution error is more appropriate. For example, a
floating-point number `1.2` should raise an execution error instead of being
may lose data, raising an _execution error_ is more appropriate. For example, a
floating-point number `1.2` should raise an _execution error_ instead of being

truncated to `1`.

If the integer internal value represents a value less than -2<sup>31</sup> or
greater than or equal to 2<sup>31</sup>, a _field error_ should be raised.
greater than or equal to 2<sup>31</sup>, an _execution error_ should be raised.

**Input Coercion**

Expand All @@ -548,12 +548,12 @@ Fields returning the type {Float} expect to encounter double-precision
floating-point internal values.

GraphQL services may coerce non-floating-point internal values to {Float} when
reasonable without losing information, otherwise they must raise a _field
reasonable without losing information, otherwise they must raise an _execution
error_. Examples of this may include returning `1.0` for the integer number `1`,
or `123.0` for the string `"123"`.

Non-finite floating-point internal values ({NaN} and {Infinity}) cannot be
coerced to {Float} and must raise a _field error_.
coerced to {Float} and must raise an _execution error_.

**Input Coercion**

Expand All @@ -579,9 +579,9 @@ that representation must be used to serialize this type.
Fields returning the type {String} expect to encounter Unicode string values.

GraphQL services may coerce non-string raw values to {String} when reasonable
without losing information, otherwise they must raise a _field error_. Examples
of this may include returning the string `"true"` for a boolean true value, or
the string `"1"` for the integer `1`.
without losing information, otherwise they must raise an _execution error_.
Examples of this may include returning the string `"true"` for a boolean true
value, or the string `"1"` for the integer `1`.

**Input Coercion**

Expand All @@ -600,8 +600,8 @@ representation of the integers `1` and `0`.
Fields returning the type {Boolean} expect to encounter boolean internal values.

GraphQL services may coerce non-boolean raw values to {Boolean} when reasonable
without losing information, otherwise they must raise a _field error_. Examples
of this may include returning `true` for non-zero numbers.
without losing information, otherwise they must raise an _execution error_.
Examples of this may include returning `true` for non-zero numbers.

**Input Coercion**

Expand All @@ -623,7 +623,7 @@ large 128-bit random numbers, to base64 encoded values, or string values of a
format like [GUID](https://en.wikipedia.org/wiki/Globally_unique_identifier).

GraphQL services should coerce as appropriate given the ID formats they expect.
When coercion is not possible they must raise a _field error_.
When coercion is not possible they must raise an _execution error_.

**Input Coercion**

Expand Down Expand Up @@ -1492,7 +1492,7 @@ enum Direction {
**Result Coercion**

GraphQL services must return one of the defined set of possible values. If a
reasonable coercion is not possible they must raise a _field error_.
reasonable coercion is not possible they must raise an _execution error_.

**Input Coercion**

Expand Down Expand Up @@ -1654,10 +1654,10 @@ is constructed with the following rules:

- If a variable is provided for an input object field, the runtime value of that
variable must be used. If the runtime value is {null} and the field type is
non-null, a _field error_ must be raised. If no runtime value is provided, the
variable definition's default value should be used. If the variable definition
does not provide a default value, the input object field definition's default
value should be used.
non-null, an _execution error_ must be raised. If no runtime value is
provided, the variable definition's default value should be used. If the
variable definition does not provide a default value, the input object field
definition's default value should be used.

Following are examples of input coercion for an input object type with a
`String` field `a` and a required (non-null) `Int!` field `b`:
Expand Down Expand Up @@ -1742,19 +1742,19 @@ brackets like this: `pets: [Pet]`. Nesting lists is allowed: `matrix: [[Int]]`.

GraphQL services must return an ordered list as the result of a list type. Each
item in the list must be the result of a result coercion of the item type. If a
reasonable coercion is not possible it must raise a _field error_. In
reasonable coercion is not possible it must raise an _execution error_. In
particular, if a non-list is returned, the coercion should fail, as this
indicates a mismatch in expectations between the type system and the
implementation.

If a list's item type is nullable, then errors occurring during preparation or
coercion of an individual item in the list must result in a the value {null} at
that position in the list along with a _field error_ added to the response. If a
list's item type is non-null, a field error occurring at an individual item in
the list must result in a field error for the entire list.
that position in the list along with an _execution error_ added to the response.
If a list's item type is non-null, an execution error occurring at an individual
item in the list must result in an execution error for the entire list.

Note: See [Handling Field Errors](#sec-Handling-Field-Errors) for more about
this behavior.
Note: See [Handling Execution Errors](#sec-Handling-Execution-Errors) for more
about this behavior.

**Input Coercion**

Expand Down Expand Up @@ -1812,12 +1812,13 @@ always optional and non-null types are always required.
In all of the above result coercions, {null} was considered a valid value. To
coerce the result of a Non-Null type, the coercion of the wrapped type should be
performed. If that result was not {null}, then the result of coercing the
Non-Null type is that result. If that result was {null}, then a _field error_
must be raised.
Non-Null type is that result. If that result was {null}, then an _execution
error_ must be raised.

Note: When a _field error_ is raised on a non-null value, the error propagates
to the parent field. For more information on this process, see
[Errors and Non-Null Fields](#sec-Executing-Selection-Sets.Errors-and-Non-Null-Fields)
Note: When an _execution error_ is raised on a non-null _response position_, the
error propagates to the parent _response position_. For more information on this
process, see
[Errors and Non-Null Types](#sec-Executing-Selection-Sets.Errors-and-Non-Null-Types)
within the Execution section.

**Input Coercion**
Expand Down
2 changes: 1 addition & 1 deletion spec/Section 5 -- Validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2010,4 +2010,4 @@ query booleanArgQueryWithDefault($booleanArg: Boolean = true) {
```

Note: The value {null} could still be provided to such a variable at runtime. A
non-null argument must raise a _field error_ if provided a {null} value.
non-null argument must raise an _execution error_ if provided a {null} value.
118 changes: 67 additions & 51 deletions spec/Section 6 -- Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ ExecuteQuery(query, schema, variableValues, initialValue):
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
queryType, initialValue, variableValues)} _normally_ (allowing
parallelization).
- Let {errors} be the list of all _field error_ raised while executing the
- Let {errors} be the list of all _execution error_ raised while executing the
selection set.
- Return an unordered map containing {data} and {errors}.

Expand All @@ -158,7 +158,7 @@ ExecuteMutation(mutation, schema, variableValues, initialValue):
- Let {selectionSet} be the top level selection set in {mutation}.
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
mutationType, initialValue, variableValues)} _serially_.
- Let {errors} be the list of all _field error_ raised while executing the
- Let {errors} be the list of all _execution error_ raised while executing the
selection set.
- Return an unordered map containing {data} and {errors}.

Expand Down Expand Up @@ -317,10 +317,10 @@ MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues):
- Complete {responseStream} normally.
- Return {responseStream}.

Note: Since {ExecuteSubscriptionEvent()} handles all _field error_, and _request
error_ only occur during {CreateSourceEventStream()}, the only remaining error
condition handled from {ExecuteSubscriptionEvent()} are internal exceptional
errors not described by this specification.
Note: Since {ExecuteSubscriptionEvent()} handles all _execution error_, and
_request error_ only occur during {CreateSourceEventStream()}, the only
remaining error condition handled from {ExecuteSubscriptionEvent()} are internal
exceptional errors not described by this specification.

ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue):

Expand All @@ -330,7 +330,7 @@ ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue):
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
subscriptionType, initialValue, variableValues)} _normally_ (allowing
parallelization).
- Let {errors} be the list of all _field error_ raised while executing the
- Let {errors} be the list of all _execution error_ raised while executing the
selection set.
- Return an unordered map containing {data} and {errors}.

Expand Down Expand Up @@ -376,17 +376,23 @@ ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues):
Note: {resultMap} is ordered by which fields appear first in the operation. This
is explained in greater detail in the Field Collection section below.

**Errors and Non-Null Fields**
**Errors and Non-Null Types**

If during {ExecuteSelectionSet()} a field with a non-null {fieldType} raises a
_field error_ then that error must propagate to this entire selection set,
either resolving to {null} if allowed or further propagated to a parent field.
<a name="sec-Executing-Selection-Sets.Errors-and-Non-Null-Fields">
<!-- Legacy link, this section was previously titled "Errors and Non-Null Fields" -->
</a>

If this occurs, any sibling fields which have not yet executed or have not yet
yielded a value may be cancelled to avoid unnecessary work.
If during {ExecuteSelectionSet()} a _response position_ with a non-null type
raises an _execution error_ then that error must propagate to the parent
response position (the entire selection set in the case of a field, or the
entire list in the case of a list position), either resolving to {null} if
allowed or being further propagated to a parent response position.

Note: See [Handling Field Errors](#sec-Handling-Field-Errors) for more about
this behavior.
If this occurs, any sibling response positions which have not yet executed or
have not yet yielded a value may be cancelled to avoid unnecessary work.

Note: See [Handling Execution Errors](#sec-Handling-Execution-Errors) for more
about this behavior.

### Normal and Serial Execution

Expand Down Expand Up @@ -646,7 +652,7 @@ CoerceArgumentValues(objectType, field, variableValues):
- Add an entry to {coercedValues} named {argumentName} with the value
{defaultValue}.
- Otherwise if {argumentType} is a Non-Nullable type, and either {hasValue} is
not {true} or {value} is {null}, raise a _field error_.
not {true} or {value} is {null}, raise an _execution error_.
- Otherwise if {hasValue} is {true}:
- If {value} is {null}:
- Add an entry to {coercedValues} named {argumentName} with the value
Expand All @@ -656,13 +662,16 @@ CoerceArgumentValues(objectType, field, variableValues):
{value}.
- Otherwise:
- If {value} cannot be coerced according to the input coercion rules of
{argumentType}, raise a _field error_.
{argumentType}, raise an _execution error_.
- Let {coercedValue} be the result of coercing {value} according to the
input coercion rules of {argumentType}.
- Add an entry to {coercedValues} named {argumentName} with the value
{coercedValue}.
- Return {coercedValues}.

Any _request error_ raised as a result of input coercion during
{CoerceArgumentValues()} should be treated instead as an _execution error_.

Note: Variable values are not coerced because they are expected to be coerced
before executing the operation in {CoerceVariableValues()}, and valid operations
must only allow usage of variables of appropriate types.
Expand Down Expand Up @@ -703,12 +712,12 @@ CompleteValue(fieldType, fields, result, variableValues):
- Let {innerType} be the inner type of {fieldType}.
- Let {completedResult} be the result of calling {CompleteValue(innerType,
fields, result, variableValues)}.
- If {completedResult} is {null}, raise a _field error_.
- If {completedResult} is {null}, raise an _execution error_.
- Return {completedResult}.
- If {result} is {null} (or another internal value similar to {null} such as
{undefined}), return {null}.
- If {fieldType} is a List type:
- If {result} is not a collection of values, raise a _field error_.
- If {result} is not a collection of values, raise an _execution error_.
- Let {innerType} be the inner type of {fieldType}.
- Return a list where each list item is the result of calling
{CompleteValue(innerType, fields, resultItem, variableValues)}, where
Expand Down Expand Up @@ -743,7 +752,7 @@ CoerceResult(leafType, value):
- Return the result of calling the internal method provided by the type system
for determining the "result coercion" of {leafType} given the value {value}.
This internal method must return a valid value for the type and not {null}.
Otherwise raise a _field error_.
Otherwise raise an _execution error_.

Note: If a field resolver returns {null} then it is handled within
{CompleteValue()} before {CoerceResult()} is called. Therefore both the input
Expand Down Expand Up @@ -798,39 +807,46 @@ MergeSelectionSets(fields):
- Append all selections in {fieldSelectionSet} to {selectionSet}.
- Return {selectionSet}.

### Handling Field Errors
### Handling Execution Errors

<a name="sec-Handling-Field-Errors">
<!-- Legacy link, this section was previously titled "Handling Execution Errors" -->
</a>

A _field error_ is an error raised from a particular field during value
resolution or coercion. While these errors should be reported in the response,
they are "handled" by producing a partial response.
An _execution error_ is an error raised during field execution, value resolution
or coercion, at a specific _response position_. While these errors must be
reported in the response, they are "handled" by producing partial {"data"} in
the _response_.

Note: This is distinct from a _request error_ which results in a response with
no data.

If a field error is raised while resolving a field, it is handled as though the
field returned {null}, and the error must be added to the {"errors"} list in the
response.

If the result of resolving a field is {null} (either because the function to
resolve the field returned {null} or because a field error was raised), and that
field is of a `Non-Null` type, then a field error is raised. The error must be
added to the {"errors"} list in the response.

If the field returns {null} because of a field error which has already been
added to the {"errors"} list in the response, the {"errors"} list must not be
further affected. That is, only one error should be added to the errors list per
field.

Since `Non-Null` type fields cannot be {null}, field errors are propagated to be
handled by the parent field. If the parent field may be {null} then it resolves
to {null}, otherwise if it is a `Non-Null` type, the field error is further
propagated to its parent field.

If a `List` type wraps a `Non-Null` type, and one of the elements of that list
resolves to {null}, then the entire list must resolve to {null}. If the `List`
type is also wrapped in a `Non-Null`, the field error continues to propagate
upwards.

If all fields from the root of the request to the source of the field error
return `Non-Null` types, then the {"data"} entry in the response should be
{null}.
If an execution error is raised while resolving a field (either directly or
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do underscores around execution error everywhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general pattern is to do it at least once per headed section, but not to worry about it after that. I generally do it when I feel it adds clarity.

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 response.

If the result of resolving a _response position_ is {null} (either due to the
result of {ResolveFieldValue()} or because an execution error was raised), and
that position is of a `Non-Null` type, then an execution error is raised at that
position. The error must be added to the {"errors"} list in the response.

If a _response position_ resolves to {null} because of an execution error which
has already been added to the {"errors"} list in the response, the {"errors"}
list must not be further affected. That is, only one error should be added to
the errors list per _response position_.

Since `Non-Null` response positions cannot be {null}, execution errors are
propagated to be handled by the parent _response position_. If the parent
response position may be {null} then it resolves to {null}, otherwise if it is a
`Non-Null` type, the execution error is further propagated to its parent
_response position_.

If a `List` type wraps a `Non-Null` type, and one of the _response position_
elements of that list resolves to {null}, then the entire list _response
position_ must resolve to {null}. If the `List` type is also wrapped in a
`Non-Null`, the execution error continues to propagate upwards.

If every _response position_ from the root of the request to the source of the
execution error has a `Non-Null` type, then the {"data"} entry in the response
should be {null}.
Loading