Note: This appendix defines an optional mechanism enabling existing fields to be
marked as Non-Null for clients that opt out of error propagation without
changing the error propagation boundaries for deployed legacy clients.
Implementations are not required to support this feature, but doing so enables
gradual migration toward semantic nullability while preserving compatibility.
With the introduction of error behavior, clients can take responsibility for
handling of execution error: correlating {"errors"} in the result with null
values inside {"data"} and thereby removing the ambiguity that error propagation
originally set out to solve. If all clients adopt this approach then schema
designers can, and should, reflect true nullability in the schema, marking
fields as Non-Null based on their data semantics without regard to whether or
not they might error.
However, legacy clients may not perform this correlation. Introducing Non-Null
in such cases could cause errors to propagate further, potentially turning a
previously handled error in a single field into a full-screen error in the
application.
To support a smooth transition, this appendix introduces the @noPropagate
directive and the concept of transitional Non-Null types. These wrappers raise
errors like regular Non-Null types, but suppress propagation and appear
nullable in introspection when using the legacy {"PROPAGATE"} error behavior.
directive @noPropagate(levels: [Int!]! = [0]) on FIELD_DEFINITIONThe @noPropagate directive instructs the system to mark the non-null types at
the given levels in the field's return type as "transitional" non-null types
(see Transitional Non-Null Type).
The levels argument identifies levels within the return type by counting each
list wrapper. Level 0 refers to the base type; each nested list increases the
level by 1 for its inner type. For the avoidance of doubt: Non-Null wrappers
do not increase the count.
If a listed level corresponds to a nullable type in the return type, it has no effect.
For a field that does not return a list type you do not need to specify levels.
If a field returns a list type and you wish to mark the inner type as
@noPropagate only then you would provide @noPropagate(levels: [1]).
This example outlines how you might introduce semantic nullability into existing
fields in your schema, to reduce the number of null checks your error-handling
clients need to perform. Remember: new fields should reflect the semantic
nullability immediately, they do not need the @noPropagate directive since
there is no legacy to support.
type Query {
- myString: String
+ myString: String! @noPropagate
- myString2: String
+ myString2: String! @noPropagate(levels: [0])
- myList: [Int]!
+ myList: [Int!]! @noPropagate(levels: [1])
}type Query {
myString: String! @noPropagate
# Equivalent to the above
myString2: String! @noPropagate(levels: [0])
myList: [Int!]! @noPropagate(levels: [1])
}A "transitional" Non-Null type is a variant of a Non-Null type that behaves identically to Non-Null with two exceptions:
- If an execution error occurs in this response position, the error does not propagate to the parent response position, instead the response position is set to {null}.
- When the error behavior of the request is {"PROPAGATE"}, this response position must be exposed as nullable in introspection.
When interpreting the Handling Execution Errors and Errors and Non-Null Types sections of the specification, Transitional Non-Null types should be treated as if they were nullable types. This does not apply to {CompleteValue()} which should still raise an execution error if {null} is returned for a Transitional Non-Null type.
Note: Transitional Non-Null types do not appear in the type system as a distinct __TypeKind. They are unwrapped to nullable types in introspection when the error behavior is {"PROPAGATE"}, and appear as {"NON_NULL"} otherwise.
__Field.type
When the request error behavior is {"PROPAGATE"}, the type field on the
__Field introspection type must return a __Type that represents the type of
value returned by this field with the transitional Non-Null wrapper types
unwrapped at every level.
__Field.noPropagateLevels
This additional field should be added to introspection:
extend type __Field {
noPropagateLevels: [Int!]
}The list must match the levels that would be passed to @noPropagate to
describe the field’s transitional Non-Null wrappers, or null if no
@noPropagate would be needed. It must not be an empty list.
When representing a GraphQL schema using the type system definition language,
any field whose return type involves Transitional Non-Null types must indicate
this via the @noPropagate directive.