Skip to content

Commit 1773c8c

Browse files
authored
fix: handle originally unexpected errors correctly (#3968)
* fix: handle originally unexpected errors correctly * Fix typings * Test * Fix * F * Remove unexpected thing * ..
1 parent 498241b commit 1773c8c

File tree

3 files changed

+87
-39
lines changed

3 files changed

+87
-39
lines changed

.changeset/deep-doodles-hang.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'graphql-yoga': patch
3+
---
4+
5+
Handle unexpected errors correctly.
6+
7+
Yoga checks originalError to see if it is a wrapped error of an unexpected error, because execution engine can wrap it multiple times.

packages/graphql-yoga/__tests__/error-masking.spec.ts

+43
Original file line numberDiff line numberDiff line change
@@ -759,4 +759,47 @@ describe('error masking', () => {
759759
['Processing GraphQL Parameters done.'],
760760
]);
761761
});
762+
763+
// Execution engine wraps errors recursively so the only way to make sure it is an unexpected error is to check originalError recursively
764+
it('respects wrapped original errors', async () => {
765+
const error = new Error('I like turtles');
766+
const wrappedError = createGraphQLError('I like tortoises', {
767+
originalError: error,
768+
});
769+
const wrappedOverWrappedError = createGraphQLError('I like animals', {
770+
originalError: wrappedError,
771+
});
772+
773+
const yoga = createYoga({
774+
schema: createSchema({
775+
typeDefs: /* GraphQL */ `
776+
type Query {
777+
a: String!
778+
}
779+
`,
780+
resolvers: {
781+
Query: {
782+
a: () => wrappedOverWrappedError,
783+
},
784+
},
785+
}),
786+
logging: false,
787+
maskedErrors: true,
788+
});
789+
790+
const response = await yoga.fetch('http://yoga/graphql', {
791+
method: 'POST',
792+
headers: { 'content-type': 'application/json' },
793+
body: JSON.stringify({ query: '{ a }' }),
794+
});
795+
796+
const body = await response.json();
797+
expect(body).toMatchObject({
798+
errors: [
799+
{
800+
message: 'Unexpected error.',
801+
},
802+
],
803+
});
804+
});
762805
});
+37-39
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,50 @@
11
import { createGraphQLError } from '@graphql-tools/utils';
2-
import { isGraphQLError } from '../error.js';
2+
import { isGraphQLError, isOriginalGraphQLError } from '../error.js';
33
import { MaskError } from '../types.js';
44

5+
function serializeError(error: unknown) {
6+
if (isGraphQLError(error)) {
7+
return error.toJSON();
8+
}
9+
if (error instanceof Error) {
10+
return {
11+
message: error.message,
12+
stack: error.stack,
13+
cause: error.cause,
14+
};
15+
}
16+
return error;
17+
}
18+
519
export const maskError: MaskError = (
620
error: unknown,
721
message: string,
822
isDev = globalThis.process?.env?.['NODE_ENV'] === 'development',
923
) => {
24+
if (isOriginalGraphQLError(error)) {
25+
return error;
26+
}
27+
const errorExtensions: Record<string, unknown> = {
28+
code: 'INTERNAL_SERVER_ERROR',
29+
unexpected: true,
30+
};
31+
const errorOptions: Parameters<typeof createGraphQLError>[1] = {
32+
extensions: errorExtensions,
33+
};
1034
if (isGraphQLError(error)) {
11-
if (error.originalError) {
12-
if (error.originalError.name === 'GraphQLError') {
13-
return error;
14-
}
15-
return createGraphQLError(message, {
16-
nodes: error.nodes,
17-
source: error.source,
18-
positions: error.positions,
19-
path: error.path,
20-
extensions: {
21-
code: 'INTERNAL_SERVER_ERROR',
22-
...error.extensions,
23-
unexpected: true,
24-
...(isDev
25-
? {
26-
originalError: {
27-
message: error.originalError.message,
28-
stack: error.originalError.stack,
29-
},
30-
}
31-
: {}),
32-
},
33-
});
35+
errorOptions.nodes = error.nodes;
36+
errorOptions.source = error.source;
37+
errorOptions.positions = error.positions;
38+
errorOptions.path = error.path;
39+
if (isDev && error.originalError) {
40+
errorExtensions['originalError'] = serializeError(error.originalError);
3441
}
35-
return error;
42+
if (error.extensions?.['http']) {
43+
errorExtensions['http'] = error.extensions['http'];
44+
}
45+
} else if (isDev) {
46+
errorExtensions['originalError'] = serializeError(error);
3647
}
3748

38-
return createGraphQLError(message, {
39-
extensions: {
40-
code: 'INTERNAL_SERVER_ERROR',
41-
unexpected: true,
42-
originalError: isDev
43-
? error instanceof Error
44-
? {
45-
message: error.message,
46-
stack: error.stack,
47-
}
48-
: error
49-
: undefined,
50-
},
51-
});
49+
return createGraphQLError(message, errorOptions);
5250
};

0 commit comments

Comments
 (0)