diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 945c916f6..7c84da9be 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -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, @@ -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 @@ -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 truncated to `1`. If the integer internal value represents a value less than -231 or -greater than or equal to 231, a _field error_ should be raised. +greater than or equal to 231, an _execution error_ should be raised. **Input Coercion** @@ -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** @@ -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** @@ -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** @@ -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** @@ -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** @@ -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`: @@ -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** @@ -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** diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 44bc9dbba..6ca9506d2 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -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. diff --git a/spec/Section 6 -- Execution.md b/spec/Section 6 -- Execution.md index 187c75a39..90cd57642 100644 --- a/spec/Section 6 -- Execution.md +++ b/spec/Section 6 -- Execution.md @@ -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}. @@ -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}. @@ -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): @@ -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}. @@ -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. + + + -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 @@ -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 @@ -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. @@ -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 @@ -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 @@ -798,39 +807,46 @@ MergeSelectionSets(fields): - Append all selections in {fieldSelectionSet} to {selectionSet}. - Return {selectionSet}. -### Handling Field Errors +### Handling Execution Errors + + + + -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 +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}. diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index daca5bb1f..6d5ee9b05 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -5,8 +5,7 @@ response. The service's response describes the result of executing the requested operation if successful, and describes any errors raised during the request. A response may contain both a partial response as well as a list of errors in -the case that any _field error_ was raised on a field and was replaced with -{null}. +the case that any _execution error_ was raised and replaced with {null}. ## Response Format @@ -18,12 +17,12 @@ A GraphQL request returns either a _response_ or a _response stream_. or mutation. A _response_ must be a map. If the request raised any errors, the response map must contain an entry with -key `errors`. The value of this entry is described in the "Errors" section. If +key {"errors"}. The value of this entry is described in the "Errors" section. If the request completed without raising any errors, this entry must not be present. If the request included execution, the response map must contain an entry with -key `data`. The value of this entry is described in the "Data" section. If the +key {"data"}. The value of this entry is described in the "Data" section. If the request failed before execution, due to a syntax error, missing information, or validation error, this entry must not be present. @@ -36,7 +35,7 @@ To ensure future changes to the protocol do not break existing services and clients, the top level response map must not contain any entries other than the three described above. -Note: When `errors` is present in the response, it may be helpful for it to +Note: When {"errors"} is present in the response, it may be helpful for it to appear first when serialized to make it more clear when errors are present in a response during debugging. @@ -45,36 +44,82 @@ response during debugging. :: A GraphQL request returns a _response stream_ when the GraphQL operation is a subscription. A _response stream_ must be a stream of _response_. +### Response Position + + + + + +:: A _response position_ is a uniquely identifiable position in the response +data produced during execution. It is either a direct entry in the {resultMap} +of a {ExecuteSelectionSet()}, or it is a position in a (potentially nested) List +value. Each response position is uniquely identifiable via a _response path_. + +:: A _response path_ uniquely identifies a _response position_ via a list of +path segments (response keys or list indices) starting at the root of the +response and ending with the associated response position. + +The value for a _response path_ must be a list of path segments. Path segments +that represent field response keys must be strings, and path segments that +represent list indices must be 0-indexed integers. 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. + +When a _response path_ is present on an _error result_, it identifies the +_response position_ which raised the error. + +A single field execution may result in multiple response positions. For example, + +```graphql example +{ + hero(episode: $episode) { + name + friends { + name + } + } +} +``` + +The hero's name would be found in the _response position_ identified by the +_response path_ `["hero", "name"]`. The List of the hero's friends would be +found at `["hero", "friends"]`, the hero's first friend at +`["hero", "friends", 0]` and that friend's name at +`["hero", "friends", 0, "name"]`. + ### Data -The `data` entry in the response will be the result of the execution of the +The {"data"} entry in the response will be the result of the execution of the requested operation. If the operation was a query, this output will be an object of the query root operation type; if the operation was a mutation, this output will be an object of the mutation root operation type. -If an error was raised before execution begins, the `data` entry should not be +The response data is the result of accumulating the resolved result of all +response positions during execution. + +If an error was raised before execution begins, the {"data"} entry should not be present in the response. If an error was raised during the execution that prevented a valid response, the -`data` entry in the response should be `null`. +{"data"} entry in the response should be `null`. ### Errors -The `errors` entry in the response is a non-empty list of errors raised during +The {"errors"} entry in the response is a non-empty list of errors raised during the _request_, where each error is a map of data described by the error result format below. -If present, the `errors` entry in the response must contain at least one error. -If no errors were raised during the request, the `errors` entry must not be -present in the response. +If present, the {"errors"} entry in the response must contain at least one +error. If no errors were raised during the request, the {"errors"} entry must +not be present in the response. -If the `data` entry in the response is not present, the `errors` entry must be -present. It must contain at least one _request error_ indicating why no data was -able to be returned. +If the {"data"} entry in the response is not present, the {"errors"} entry must +be present. It must contain at least one _request error_ indicating why no data +was able to be returned. -If the `data` entry in the response is present (including if it is the value -{null}), the `errors` entry must be present if and only if one or more _field -error_ was raised during execution. +If the {"data"} entry in the response is present (including if it is the value +{null}), the {"errors"} entry must be present if and only if one or more +_execution error_ was raised during execution. **Request Errors** @@ -85,40 +130,54 @@ to determine which operation to execute, or invalid input values for variables. A request error is typically the fault of the requesting client. -If a request error is raised, the `data` entry in the response must not be -present, the `errors` entry must include the error, and request execution should -be halted. +If a request error is raised, the {"data"} entry in the response must not be +present, the {"errors"} entry must include the error, and request execution +should be halted. + +**Execution Errors** -**Field Errors** + + + -:: A _field error_ is an error raised during the execution of a particular field -which results in partial response data. This may occur due to an internal error -during value resolution or failure to coerce the resulting value. +:: An _execution error_ is an error raised during the execution of a particular +field which results in partial response data. This may occur due to failure to +coerce the arguments for the field, an internal error during value resolution, +or failure to coerce the resulting value. -A field error is typically the fault of a GraphQL service. +Note: In previous versions of this specification _execution error_ was called +_field error_. -If a field error is raised, execution attempts to continue and a partial result -is produced (see [Handling Field Errors](#sec-Handling-Field-Errors)). The -`data` entry in the response must be present. The `errors` entry should include -this error. +An execution error is typically the fault of a GraphQL service. + +An _execution error_ must occur at a specific _response position_, and may occur +in any response position. The response position of an execution error is +indicated via a _response path_ in the error response's {"path"} entry. + +When an execution error is raised at a given _response position_, then that +response position must not be present within the _response_ {"data"} entry +(except {null}), and the {"errors"} entry must include the error. Nested +execution is halted and sibling execution attempts to continue, producing +partial result (see +[Handling Execution Errors](#sec-Handling-Execution-Errors)). **Error Result Format** -Every error must contain an entry with the key `message` with a string +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. 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. +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. If an error can be associated to a particular field in the GraphQL result, it -must contain an entry with the key `path` that details the path of the response -field which experienced the error. This allows clients to identify whether a -`null` result is intentional or caused by a runtime error. The value of this -_path entry_ is described in the [Path](#sec-Path) section. +must contain an entry with the key {"path"} with a _response path_ which +describes the _response position_ which raised the error. This allows clients to +identify whether a {null} resolved result is a true value or the result of an +_execution error_. For example, if fetching one of the friends' names fails in the following operation: @@ -248,21 +307,6 @@ discouraged. } ``` -### Path - -:: A _path entry_ is an entry within an _error result_ that allows for -association with a particular field reached during GraphQL execution. - -The value for a _path entry_ must be a list of path segments starting at the -root of the response and ending with the field to be associated with. Path -segments that represent fields must be strings, and path segments that represent -list indices must be 0-indexed integers. 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. - -When the _path entry_ is present on an _error result_, it identifies the -response field which experienced the error. - ## Serialization Format GraphQL does not require a specific serialization format. However, clients