GraphQLClientError
Serializable GraphQL client errors with an operation name and a
Schema.TaggedError reason.
Errors
The response was not a GraphQL response, or data did not match the result
Schema.
Details
data: null without errors also produces a DecodeError, since
ResponseError requires a non-empty errors array.
Signature
declare class DecodeError extends { readonly _tag: "DecodeError"; readonly description: string;} & YieldableError<this> { constructor(...args: [props: { readonly _tag?: "DecodeError"; readonly description: string; }, options?: MakeOptions]); isRetryable: boolean; message: string;}The variables Schema rejected the input, so nothing was sent.
Signature
declare class EncodeError extends { readonly _tag: "EncodeError"; readonly description: string;} & YieldableError<this> { constructor(...args: [props: { readonly _tag?: "EncodeError"; readonly description: string; }, options?: MakeOptions]); isRetryable: boolean; message: string;}The error every GraphQLClient method fails with. It names the operation
and carries one of the five reasons; cause is the reason itself.
Signature
declare class GraphQLClientError extends { readonly _tag: "GraphQLClientError"; readonly operation: string; readonly reason: ResponseError | TransportError | EncodeError | DecodeError | PaginationError;} & YieldableError<this> { constructor(...args: [props: { readonly _tag?: "GraphQLClientError"; readonly operation: string; readonly reason: ResponseError | TransportError | EncodeError | DecodeError | PaginationError; }, options?: MakeOptions]); readonly "~effect/graphql/GraphQLClientError": "~effect/graphql/GraphQLClientError"; readonly cause: ResponseError | TransportError | EncodeError | DecodeError | PaginationError; headers: Headers | undefined; isRetryable: boolean; message: string; retryAfter: Duration | undefined; status: number | undefined;}Example Retrying only what is retryable
import { Duration, Schema } from "effect"import { GraphQLClientError } from "effect/graphql"
const error = new GraphQLClientError.GraphQLClientError({ operation: "Viewer", reason: new GraphQLClientError.TransportError({ description: "503 Service Unavailable", status: 503, retryAfter: Duration.seconds(30) })})
error.isRetryable // => trueerror.retryAfter // => Duration.seconds(30)
// Errors round-trip through their Schemaconst encoded = Schema.encodeSync(GraphQLClientError.GraphQLClientError)(error)Schema.decodeUnknownSync(GraphQLClientError.GraphQLClientError)(encoded).reason._tag // => "TransportError"Cursor paging could not continue. Only GraphQLClient.pages and
GraphQLClient.items raise it, when a connection becomes null on a page
after the first, or when hasNextPage is true but endCursor is null
or did not advance.
Details
cursor is the last cursor that was sent, null on the first page.
Signature
declare class PaginationError extends { readonly _tag: "PaginationError"; readonly cursor: string | null; readonly description: string;} & YieldableError<this> { constructor(...args: [props: { readonly _tag?: "PaginationError"; readonly cursor: string | null; readonly description: string; }, options?: MakeOptions]); isRetryable: boolean; message: string;}The server answered with a non-empty errors array.
Details
Queries and mutations fail even when partial data is present, since a
field error's null cannot be distinguished from a genuine null. Use
{ partial: true } to receive decoded { data, errors } instead.
On this error, data is raw. GraphQLError.path[0] identifies the response
key (or alias). Non-2xx HTTP responses with a GraphQL body also use this
reason rather than TransportError.
status and headers are the HTTP status and response headers, as the
transport reported them and middleware left them. Headers use
Headers.Headers at runtime and encode as a plain string record.
Decoding restores the normalized header collection. Both are unset over
graphql-ws, and status is unset for graphql-sse events. retryAfter
parses the Retry-After response header as whole seconds or an HTTP date,
measured from Date.now() when the error is constructed. isRetryable is
true for HTTP status 429 or 500โ599, and for any response with a
valid Retry-After, whatever its status. It is false otherwise, including
when there is no status.
For queries and mutations, callers opt into retries with Effect.retry,
and should wait for retryAfter when it is set. With
GraphQLClient.pages, retry each page's call, not the stream:
Stream.retry starts again from the first page. Subscriptions retry a
retryable ResponseError on their own, by resubscribing. graphql-sse events
carry the stream response's headers, so an event with errors is retried
when that response had a valid Retry-After, and ends the stream
otherwise.
Signature
declare class ResponseError extends { readonly _tag: "ResponseError"; readonly data?: Json; readonly errors: readonly [{ [key: string]: Json; readonly extensions?: { [key: string]: Json; }; readonly locations?: readonly Array<{ readonly column: number; readonly line: number; }>; readonly message: string; readonly path?: readonly Array<string | number>; }, { [key: string]: Json; readonly extensions?: { [key: string]: Json; }; readonly locations?: readonly Array<{ readonly column: number; readonly line: number; }>; readonly message: string; readonly path?: readonly Array<string | number>; }]; readonly extensions?: { [key: string]: Json; }; readonly headers?: Headers; readonly status?: number;} & YieldableError<this> { constructor(...args: [props: { readonly _tag?: "ResponseError"; readonly data?: unknown; readonly errors: readonly [{ [key: string]: unknown; readonly extensions?: { [key: string]: unknown; }; readonly locations?: readonly Array<{ readonly column: number; readonly line: number; }>; readonly message: string; readonly path?: readonly Array<string | number>; }, { [key: string]: unknown; readonly extensions?: { [key: string]: unknown; }; readonly locations?: readonly Array<{ readonly column: number; readonly line: number; }>; readonly message: string; readonly path?: readonly Array<string | number>; }]; readonly extensions?: { [key: string]: unknown; }; readonly headers?: Headers; readonly status?: number; }, options?: MakeOptions]); isRetryable: boolean; message: string; retryAfter: Duration | undefined;}Example Inspecting the errors of a failed call
import { GraphQLClientError } from "effect/graphql"
const reason = new GraphQLClientError.ResponseError({ errors: [{ message: "Not found", path: ["repository"] }], data: { repository: null }, status: 200})
reason.isRetryable // => falsereason.status // => 200reason.errors.map((error) => error.path?.[0]) // => ["repository"]Example Retrying a call on retryable errors
import { Effect } from "effect"import { GraphQLClientError } from "effect/graphql"
const call = Effect.fail(new GraphQLClientError.GraphQLClientError({ operation: "Viewer", reason: new GraphQLClientError.ResponseError({ errors: [{ message: "Query timed out" }], status: 502 })}))
const retried = Effect.retry(call, { while: (error) => error.reason.isRetryable, times: 3})The client could not get a GraphQL response from the server: a network failure, an HTTP response that is not a GraphQL response, or a closed WebSocket connection.
Details
statusandheadersare only set for an HTTP response that is not a GraphQL response. Headers useHeaders.Headersat runtime and encode as a plain string record; decoding restores the header collection.retryAftercomes from theRetry-Afterheader (whole seconds or an HTTP date) and is encoded as milliseconds. When it is set, the error is retryable at any status, unless the close code is fatal.closeCodeis set when a WebSocket connection closed. See isFatalCloseCode for the codes that are not retryable.fatal: truemarks a failure that retrying cannot fix, such as an operation on a graphql-ws transport whose scope has closed.causeholds the original error, such as theHttpClientError. Once encoded it shrinks to{ name, message }, the standardSchema.Defectbehaviour.
Signature
declare class TransportError extends { readonly _tag: "TransportError"; readonly cause?: unknown; readonly closeCode?: number; readonly description: string; readonly fatal?: boolean; readonly headers?: Headers; readonly retryAfter?: Duration; readonly status?: number;} & YieldableError<this> { constructor(...args: [props: { readonly _tag?: "TransportError"; readonly cause?: unknown; readonly closeCode?: number; readonly description: string; readonly fatal?: boolean; readonly headers?: Headers; readonly retryAfter?: Duration; readonly status?: number; }, options?: MakeOptions]); isRetryable: boolean; message: string; static fromHttpClientError(error: HttpClientError, options?: { readonly now?: number; }): TransportError;}Example Retryability of transport failures
import { GraphQLClientError } from "effect/graphql"
new GraphQLClientError.TransportError({ description: "ECONNREFUSED" }).isRetryable // => truenew GraphQLClientError.TransportError({ description: "503", status: 503 }).isRetryable // => truenew GraphQLClientError.TransportError({ description: "404", status: 404 }).isRetryable // => falsenew GraphQLClientError.TransportError({ description: "closed", closeCode: 4401 }).isRetryable // => falsenew GraphQLClientError.TransportError({ description: "shut down", fatal: true }).isRetryable // => falseGuards
Tests whether a value is a GraphQLClientError.
Signature
declare function isGraphQLClientError(u: unknown): u is GraphQLClientErrorModels
GraphQLError
typeThe decoded type of GraphQLError.
Signature
type GraphQLError = typeof GraphQLError.TypeThe union of every reason a GraphQLClientError can carry.
Signature
type Reason = ResponseError | TransportError | EncodeError | DecodeError | PaginationErrorPredicates
Tests whether a WebSocket close code is fatal, meaning a TransportError
carrying it is not retryable.
Details
Fatal codes are every standard 1xxx code except 1000, 1001, 1005,
1006, 1012, 1013 and 1014, plus the graphql-ws codes 4400 (bad
request), 4401 (unauthorized), 4403 (forbidden), 4406 (subprotocol
not acceptable), 4409 (subscriber already exists), 4429 (too many
initialisation requests) and 4500 (internal server error). Any other code
is retryable.
4403 is fatal because reconnecting with the same credentials is refused
again.
Signature
declare function isFatalCloseCode(code: number): booleanExample Checking close codes
import { GraphQLClientError } from "effect/graphql"
GraphQLClientError.isFatalCloseCode(1006) // => falseGraphQLClientError.isFatalCloseCode(1011) // => trueGraphQLClientError.isFatalCloseCode(4401) // => trueSchemas
One entry of a GraphQL response's errors array, as described by the
GraphQL specification.
Details
path[0] is the response key the error belongs to, which is the alias when
the document used one, so callers can match an error to the field it hit.
Unknown top-level fields are preserved as JSON values, including GitHub's
type field.
Signature
declare const GraphQLError: StructWithRest<Struct<{ readonly extensions: optional<$Record<String, Codec<Json, Json, never, never>>>; readonly locations: optional<$Array<Struct<{ readonly column: Number; readonly line: Number; }>>>; readonly message: String; readonly path: optional<$Array<Union<readonly [String, Number]>>>;}>, readonly [$Record<String, Codec<Json, Json, never, never>>]>Example Finding which alias an error belongs to
import { Schema } from "effect"import { GraphQLClientError } from "effect/graphql"
const error = Schema.decodeUnknownSync(GraphQLClientError.GraphQLError)({ message: "Could not resolve to a Repository", path: ["missing", "issues"], type: "NOT_FOUND"})
error.path?.[0] // => "missing"