I believe the problem lies in 1 of 2 places, or potentially both:
thisenrichError here when it copies the extensions to a copy object (expanded) appears to spread extensions into an expanded.extensions object and spread it into the expanded.extensions.exception object, as well as any properties from originalError.To start, the only semi-related issue I could find was this one: https://github.com/apollographql/apollo-server/issues/2917
This issue does mention something relevant to this issue regarding the extensions passed to ApolloError() being replicated into the extensions.exception object: https://github.com/apollographql/apollo-server/issues/2917#issuecomment-516407421 which is true, albeit undersireable.
But overall, I'm part of an organization that is developing a set of error classes to standardize the errors we return to calling clients of our graphql APIs. These error classes extend from ApolloError. I've also been referencing the Apollo Error handling documentation, specifically the bit around customizing or creating other errors. Using the the ApolloError constructor has been problematic however. If you pass an object containing keys you want to appear in the extensions object to the ApolloError() constructor's third argument the keys in that object get duplicated into the extensions object and the extensions.exception object. This makes adhering to an error format difficult since keys are unecessarily duplicated. Especially when you consider Apollo Server creates extensions.exception.stacktrace in non-prod environments (ie. debug: true or NODE_ENV !== 'production') and if you are in prod, and throw an ApolloError with the third argument populated, your error response will contain an extensions.exception.<key you provided> instead of not being present as one would expect
In summary throw new ApolloError('some message', 'SOME_ERROR', { foo: 'bar'}) or if I define an error class like so:
class SomeCustomError extends ApolloError {
constructor(message, extensions) {
super(message, 'SOME_CUSTOM_ERROR', extensions)
}
}
and then throw new SomeCustomError('message', { foo: 'bar'})
The foo key would only show up in the extensions object as part of an individual error object:
{
message: 'some message',
...,
extensions: {
code: 'SOME_CODE',
foo: 'bar',
// if non-prod
exception: {
stacktrace: ' .... '
}
}
The foo key is duplicated. Once in the extensions object and once in the extensions.exception object like so:
non-prod:
{
message: 'some message',
...,
extensions: {
code: 'SOME_CODE',
foo: 'bar',
exception: {
foo: 'bar',
stacktrace: ' .... '
}
}
production:
{
message: 'some message',
...,
extensions: {
code: 'SOME_CODE',
foo: 'bar',
exception: {
foo: 'bar'
}
}
I'm not all that familiar with codesandbox, so please let me know if sharing the below link doesn't take you to the reproducible problem.
https://codesandbox.io/s/long-fast-wnmdu
If you run query { hello } in the playground in the above link, the hello resolver throws an ApolloError like so:
throw new ApolloError('some message', 'SOME_CODE', { keyThatWillBeDupd: 'value' });
and results in keyThatWillBeDupd being duplicated both under the extensions object but also under the extensions.exception object as shown in the below response:
{
"errors": [
{
"message": "some message",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"hello"
],
"extensions": {
"keyThatWillBeDupd": "value",
"code": "SOME_CODE",
"exception": {
"keyThatWillBeDupd": "value",
"stacktrace": [
"Error: some message",
" at hello (/sandbox/index.js:14:13)",
" at field.resolve (/sandbox/node_modules/graphql-extensions/dist/index.js:133:26)",
" at resolveFieldValueOrError (/sandbox/node_modules/graphql/execution/execute.js:467:18)",
" at resolveField (/sandbox/node_modules/graphql/execution/execute.js:434:16)",
" at executeFields (/sandbox/node_modules/graphql/execution/execute.js:275:18)",
" at executeOperation (/sandbox/node_modules/graphql/execution/execute.js:219:122)",
" at executeImpl (/sandbox/node_modules/graphql/execution/execute.js:104:14)",
" at Object.execute (/sandbox/node_modules/graphql/execution/execute.js:64:35)",
" at /sandbox/node_modules/apollo-server-core/dist/requestPipeline.js:240:46",
" at Generator.next (<anonymous>)"
]
}
}
}
],
"data": {
"hello": null
}
}
Any update on this ? I see the duplicated key in extensions and exception
Any update on this? Still active in @^2.19.0 馃槩
Based on the tagging, I believe the Apollo maintainers view this as a breaking change. So it wouldn't be fixed until a 3.0 release. I do question whether fixing this should be categorized as a breaking change, however. Unfortunately not my call.
Most helpful comment
Any update on this ? I see the duplicated key in extensions and exception