Error Handling
Making errors actionable on the client and server
Apollo Server v4 introduced a regression where providing invalid variables yields a 200 status code instead of 400. To mitigate this regression, provide the
status400ForVariableCoercionErrors: true
option to yourApolloServer
constructor. For more information, see the migration guide.
Whenever Apollo Server encounters errors while processing a GraphQL operation, its response to the client includes an errors
array containing each error that occurred. Each error in the array has an extensions
field that provides additional useful information, including an error code
and (while in development mode) a stacktrace
.
Here's an example error response caused by misspelling the __typename
field in a query:
Click to expand
1{
2 "errors": [
3 {
4 "message": "Cannot query field \"__typenam\" on type \"Query\".",
5 "locations": [
6 {
7 "line": 1,
8 "column": 2
9 }
10 ],
11 "extensions": {
12 "code": "GRAPHQL_VALIDATION_FAILED",
13 "stacktrace": [
14 "GraphQLError: Cannot query field \"__typenam\" on type \"Query\".",
15 " at Object.Field (/my_project/node_modules/graphql/validation/rules/FieldsOnCorrectTypeRule.js:48:31)",
16 " ...additional lines..."
17 ]
18 }
19 }
20 ]
21}
To help with debugging, Apollo Server provides an ApolloServerErrorCode
enum, which you can use to check if your error is one of the different types produced by Apollo Server.
You can check an error's code
to determine why an error occurred and also add logic to respond to different types of errors, like so:
1import { ApolloServerErrorCode } from '@apollo/server/errors';
2
3if (error.extensions?.code === ApolloServerErrorCode.GRAPHQL_PARSE_FAILED) {
4 // respond to the syntax error
5} else if (error.extensions?.code === "MY_CUSTOM_CODE") {
6 // do something else
7}
Apollo Server's variety of error codes enables requesting clients to respond differently to different error types. You can also create your own custom errors and codes.
Built-in error codes
Code | Description |
---|---|
| The GraphQL operation string contains a syntax error. |
| The GraphQL operation is not valid against the server's schema. |
| The GraphQL operation includes an invalid value for a field argument. |
| A client sent the hash of a query string to execute via automatic persisted queries, but the query was not in the APQ cache. |
| A client sent the hash of a query string to execute via automatic persisted queries, but the server has disabled APQ. |
| The request was parsed successfully and is valid against the server's schema, but the server couldn't resolve which operation to run.This occurs when a request containing multiple named operations doesn't specify which operation to run (i.e.,operationName ), or if the named operation isn't included in the request. |
| An error occurred before your server could attempt to parse the given GraphQL operation. |
| An unspecified error occurred.When Apollo Server formats an error in a response, it sets the code extension to this value if no other code is set. |
Custom errors
You can create a custom errors and codes using the graphql
package's GraphQLError
class, like so:
1import { GraphQLError } from 'graphql';
2
3throw new GraphQLError(message, {
4 extensions: { code: 'YOUR_ERROR_CODE', myCustomExtensions },
5});
Custom errors can provide additional context, enabling your clients to understand why an error is happening. We recommend making clear errors for common cases, for example, when a user isn't logged in (UNAUTHENTICATED
), or someone is forbidden from performing an action:
1import { GraphQLError } from 'graphql';
2
3throw new GraphQLError('You are not authorized to perform this action.', {
4 extensions: {
5 code: 'FORBIDDEN',
6 },
7});
Throwing errors
Apollo Server throws errors automatically when applicable. For example, it throws a GRAPHQL_VALIDATION_FAILED
error whenever an incoming operation isn't valid against the server's schema.
Your resolvers can also throw errors in situations where Apollo Server doesn't do so automatically.
For example, this resolver throws a custom error if the integer value provided for a user's ID is less than 1
:
Click to expand
1import { GraphQLError } from 'graphql';
2
3const typeDefs = `#graphql
4 type Query {
5 userWithID(id: ID!): User
6 }
7
8 type User {
9 id: ID!
10 name: String!
11 }
12`;
13
14const resolvers = {
15 Query: {
16 userWithID: (_, args) => {
17 if (args.id < 1) {
18 throw new GraphQLError('Invalid argument value', {
19 extensions: {
20 code: 'BAD_USER_INPUT',
21 },
22 });
23 }
24 // ...fetch correct user...
25 },
26 },
27};
If a resolver throws a generic error that is not a GraphQLError
instance, that error is still thrown with an extensions
field that includes a stacktrace
and code
(specifically INTERNAL_SERVER_ERROR
), along with any other relevant error details.
Including custom error details
Whenever you throw a GraphQLError
, you can add arbitrary fields to the error's extensions
object to provide additional context to the client. You specify these fields in an object you provide to the error's constructor.
This example builds on the one above by adding the name of the GraphQL argument that was invalid:
Click to expand
1import { GraphQLError } from 'graphql';
2
3const typeDefs = `#graphql
4 type Query {
5 userWithID(id: ID!): User
6 }
7
8 type User {
9 id: ID!
10 name: String!
11 }
12`;
13
14const resolvers = {
15 Query: {
16 userWithID: (_, args) => {
17 if (args.id < 1) {
18 throw new GraphQLError('Invalid argument value', {
19 extensions: {
20 code: 'BAD_USER_INPUT',
21 argumentName: 'id',
22 },
23 });
24 }
25 // ...fetch correct user...
26 },
27 },
28};
This results in a response like the following:
Click to expand
1{
2 "errors": [
3 {
4 "message": "Invalid argument value",
5 "locations": [
6 {
7 "line": 2,
8 "column": 3
9 }
10 ],
11 "path": ["userWithID"],
12 "extensions": {
13 "code": "BAD_USER_INPUT",
14 "argumentName": "id",
15 "stacktrace": [
16 "GraphQLError: Invalid argument value",
17 " at userWithID (/my-project/index.js:25:13)",
18 " ...more lines..."
19 ]
20 }
21 }
22 ]
23}
Omitting or including stacktrace
The stacktrace
error field is useful while developing and debugging your server, but you probably don't want to expose it to clients in production.
By default, Apollo Server omits the stacktrace
field if the NODE_ENV
environment variable is set to either production
or test
.
You can override this default behavior by passing the includeStacktraceInErrorResponses
option to the constructor of ApolloServer
. If includeStacktraceInErrorResponses
is true
, stacktrace
is always included. If it's false
, stacktrace
is always omitted.
Note that when stacktrace
is omitted, it's also unavailable to your application. To log error stacktrace
s without including them in responses to clients, see Masking and logging errors.