Skip to content

Error from GraphQLScalarType is not properly propagated to formatError hook #7178

@junajan

Description

@junajan

Hello,
we have a problem when throwing a GraphQLError from GraphQLScalarType in the new apollo v4. Before (in v3) it properly propagated the error into formatError hook but now it always changes its extensions.code to BAD_USER_INPUT and removes all additional fields.

This is happening only on errors thrown from GraphQLScalarType (e.g. errors from mutation/query resolvers work just fine) and only when we define GQL schema using GraphQLSchema. I tried this example where the schema is defined using typeDefs and resolvers keys and it also worked.

Here is the code where you can reproduce the error.
const { unwrapResolverError } = require('@apollo/server/errors');
const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone');
const { GraphQLScalarType, GraphQLError } = require('graphql');
const { GraphQLSchema, GraphQLObjectType, GraphQLInputObjectType } = require('graphql');

const ColorScalarField = new GraphQLScalarType({
  name: 'ScalarField',
  serialize: (value) => value,
  parseValue: () => {
    console.log("Serializing")

    throw new GraphQLError('Scalar field custom error', {
      extensions: { code: 'CUSTOM_ERROR_CODE', extraErrorField: 123 },
    });
  },
  parseValue: () => {
    console.log("Parsing value")

    throw new GraphQLError('Scalar field custom error', {
      extensions: { code: 'CUSTOM_ERROR_CODE', extraErrorField: 123 },
    });
  },
  parseLiteral: () => {
    console.log("Parsing literal")

    throw new GraphQLError('Scalar field custom error', {
      extensions: { code: 'CUSTOM_ERROR_CODE', extraErrorField: 123 },
    });
  },
});

const ItemType = new GraphQLObjectType({
  name: 'item',
  fields: () => ({
    color: {
      type: ColorScalarField,
    },
  }),
});

const ItemUpdateType = new GraphQLInputObjectType({
  name: 'ItemUpdate',
  fields: () => ({
    color: {
      type: ColorScalarField,
    },
  }),
});

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: () => ({
      item: {
        type: ItemType,
        description: 'Fetch the logged in user.',
        resolve: () => {},
      }
    }),
  }),
  mutation: new GraphQLObjectType({
    name: 'Mutation',
    fields: () => ({
      itemUpdate: {
        name: 'itemUpdate',
        args: {
          input: {
            type: ItemUpdateType,
          },
        },
        type: ItemType,
        resolve: () => {},
      }
    }),
  }),
});

const server = new ApolloServer({
  schema,
  debug: true,
  formatError: (err, err2) => {
    console.log("FORMAT ERROR:", err, err2)
    console.log("UNWRAPPED ERROR:", unwrapResolverError(err), unwrapResolverError(err2))

    return { err: 123 };
  }
});

(async () => {
  const { url } = await startStandaloneServer(server);
  console.log(`🚀 Server listening at: ${url}`);

  const response = await server.executeOperation({
    query: `
      mutation itemUpdate($input: ItemUpdate!) {
        itemUpdate(input: $input) {
          color
        }
      }
    `,
    variables: {
      input: {
        color: 'red',
      },
    },
  });

  console.log(response.body.singleResult.data)
  console.log(response.body.singleResult.errors)
})();
And here is the output.
Parsing value
FORMAT ERROR: {
  message: 'Variable "$input" got invalid value "red" at "input.color"; Scalar field custom error',
  locations: [ { line: 2, column: 27 } ],
  extensions: {
    code: 'BAD_USER_INPUT',
    stacktrace: [
      'GraphQLError: Variable "$input" got invalid value "red" at "input.color"; Scalar field custom error',
      '    at /test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:147:11',
      '    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:154:9)',
      '    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:117:34)',
      '    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:49:14)',
      '    at coerceInputValue (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:32:10)',
      '    at coerceVariableValues (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:132:69)',
      '    at getVariableValues (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:45:21)',
      '    at buildExecutionContext (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/execute.js:280:63)',
      '    at execute (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/execute.js:116:22)',
      '    at executeIncrementally (/test/node_modules/.pnpm/@[email protected][email protected]/node_modules/@apollo/server/dist/cjs/incrementalDeliveryPolyfill.js:47:34)'
    ]
  }
} GraphQLError: Variable "$input" got invalid value "red" at "input.color"; Scalar field custom error
    at /test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:147:11
    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:154:9)
    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:117:34)
    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:49:14)
    at coerceInputValue (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:32:10)
    at coerceVariableValues (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:132:69)
    at getVariableValues (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:45:21)
    at buildExecutionContext (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/execute.js:280:63)
    at execute (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/execute.js:116:22)
    at executeIncrementally (/test/node_modules/.pnpm/@[email protected][email protected]/node_modules/@apollo/server/dist/cjs/incrementalDeliveryPolyfill.js:47:34) {
  path: undefined,
  locations: [ { line: 2, column: 27 } ],
  extensions: { code: 'BAD_USER_INPUT' }
}
UNWRAPPED ERROR: {
  message: 'Variable "$input" got invalid value "red" at "input.color"; Scalar field custom error',
  locations: [ { line: 2, column: 27 } ],
  extensions: {
    code: 'BAD_USER_INPUT',
    stacktrace: [
      'GraphQLError: Variable "$input" got invalid value "red" at "input.color"; Scalar field custom error',
      '    at /test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:147:11',
      '    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:154:9)',
      '    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:117:34)',
      '    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:49:14)',
      '    at coerceInputValue (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:32:10)',
      '    at coerceVariableValues (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:132:69)',
      '    at getVariableValues (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:45:21)',
      '    at buildExecutionContext (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/execute.js:280:63)',
      '    at execute (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/execute.js:116:22)',
      '    at executeIncrementally (/test/node_modules/.pnpm/@[email protected][email protected]/node_modules/@apollo/server/dist/cjs/incrementalDeliveryPolyfill.js:47:34)'
    ]
  }
} GraphQLError: Variable "$input" got invalid value "red" at "input.color"; Scalar field custom error
    at /test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:147:11
    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:154:9)
    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:117:34)
    at coerceInputValueImpl (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:49:14)
    at coerceInputValue (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/utilities/coerceInputValue.js:32:10)
    at coerceVariableValues (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:132:69)
    at getVariableValues (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/values.js:45:21)
    at buildExecutionContext (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/execute.js:280:63)
    at execute (/test/node_modules/.pnpm/[email protected]/node_modules/graphql/execution/execute.js:116:22)
    at executeIncrementally (/test/node_modules/.pnpm/@[email protected][email protected]/node_modules/@apollo/server/dist/cjs/incrementalDeliveryPolyfill.js:47:34) {
  path: undefined,
  locations: [ { line: 2, column: 27 } ],
  extensions: { code: 'BAD_USER_INPUT' }
}
undefined
[ { err: 123 } ]

I would expect the error received in formatError hook to have extensions.code === 'CUSTOM_ERROR_CODE' and extensions.extraErrorField === 123.

Is there anything we missed? Thank you for any help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions