Skip to content

Commit cb864ad

Browse files
committed
fix(executor): handle AggregateErrors correctly
1 parent 34e3e11 commit cb864ad

File tree

3 files changed

+68
-19
lines changed

3 files changed

+68
-19
lines changed

.changeset/wise-pans-sing.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-tools/executor': patch
3+
---
4+
5+
Fix handling of AggregateError

packages/executor/src/execution/__tests__/error-handling.test.ts

+47-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createServer, Server } from 'http';
22
import { AddressInfo } from 'net';
33
import { parse } from 'graphql';
44
import { makeExecutableSchema } from '@graphql-tools/schema';
5-
import { isAsyncIterable } from '@graphql-tools/utils';
5+
import { createGraphQLError, isAsyncIterable } from '@graphql-tools/utils';
66
import { normalizedExecutor } from '../normalizedExecutor';
77

88
describe('Error Handling', () => {
@@ -35,15 +35,18 @@ describe('Error Handling', () => {
3535
expect(result.errors?.[0]?.message).toBe('This is not an error instance');
3636
});
3737
if (globalThis.fetch != null) {
38-
let server: Server;
38+
let server: Server | undefined;
3939
afterEach(async () => {
40-
if (!server) {
40+
if (!server?.listening) {
4141
return;
4242
}
4343
if (!globalThis.Bun) {
4444
server.closeAllConnections();
4545
}
4646
await new Promise<void>((resolve, reject) => {
47+
if (!server) {
48+
return resolve();
49+
}
4750
server.close(err => (err ? reject(err) : resolve()));
4851
});
4952
});
@@ -52,6 +55,9 @@ describe('Error Handling', () => {
5255
res.end('{ "myData": "foo"');
5356
});
5457
await new Promise<void>(resolve => {
58+
if (!server) {
59+
throw new Error('Server is not initialized');
60+
}
5561
server.listen(0, resolve);
5662
});
5763
const serverPort = (server.address() as AddressInfo).port;
@@ -94,4 +100,42 @@ describe('Error Handling', () => {
94100
}
95101
});
96102
}
103+
it('handles aggregated errors', async () => {
104+
const schema = makeExecutableSchema({
105+
typeDefs: /* GraphQL */ `
106+
type Query {
107+
throwMe: String
108+
}
109+
`,
110+
resolvers: {
111+
Query: {
112+
throwMe: () =>
113+
new AggregateError(
114+
[new Error('This is an error'), new Error('This is another error')],
115+
'This is an aggregated error',
116+
),
117+
},
118+
},
119+
});
120+
const result = await normalizedExecutor({
121+
schema,
122+
document: parse(/* GraphQL */ `
123+
query {
124+
throwMe
125+
}
126+
`),
127+
});
128+
if (isAsyncIterable(result)) {
129+
throw new Error('Expected a result, but got an async iterable');
130+
}
131+
expect(result).toEqual({
132+
data: {
133+
throwMe: null,
134+
},
135+
errors: [
136+
createGraphQLError('This is an error', {}),
137+
createGraphQLError('This is another error', {}),
138+
],
139+
});
140+
});
97141
});

packages/executor/src/execution/execute.ts

+16-16
Original file line numberDiff line numberDiff line change
@@ -743,15 +743,14 @@ function executeField(
743743
// to take a second callback for the error case.
744744
return completed.then(undefined, rawError => {
745745
if (rawError instanceof AggregateError) {
746-
return new AggregateError(
747-
rawError.errors.map(rawErrorItem => {
748-
rawErrorItem = coerceError(rawErrorItem);
749-
const error = locatedError(rawErrorItem, fieldNodes, pathToArray(path));
750-
const handledError = handleFieldError(error, returnType, errors);
751-
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
752-
return handledError;
753-
}),
754-
);
746+
let result: unknown;
747+
for (let rawErrorItem of rawError.errors) {
748+
rawErrorItem = coerceError(rawErrorItem);
749+
const error = locatedError(rawErrorItem, fieldNodes, pathToArray(path));
750+
result = handleFieldError(error, returnType, errors);
751+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
752+
}
753+
return result;
755754
}
756755
rawError = coerceError(rawError);
757756
const error = locatedError(rawError, fieldNodes, pathToArray(path));
@@ -763,13 +762,14 @@ function executeField(
763762
return completed;
764763
} catch (rawError) {
765764
if (rawError instanceof AggregateError) {
766-
return new AggregateError(
767-
rawError.errors.map(rawErrorItem => {
768-
const coercedError = coerceError(rawErrorItem);
769-
const error = locatedError(coercedError, fieldNodes, pathToArray(path));
770-
return handleFieldError(error, returnType, errors);
771-
}),
772-
);
765+
let result: unknown;
766+
for (let rawErrorItem of rawError.errors) {
767+
rawErrorItem = coerceError(rawErrorItem);
768+
const error = locatedError(rawErrorItem, fieldNodes, pathToArray(path));
769+
result = handleFieldError(error, returnType, errors);
770+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
771+
}
772+
return result;
773773
}
774774
const coercedError = coerceError(rawError);
775775
const error = locatedError(coercedError, fieldNodes, pathToArray(path));

0 commit comments

Comments
 (0)