-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inspect non-error types to produce helpful error messages for failing resolvers #1600
Inspect non-error types to produce helpful error messages for failing resolvers #1600
Conversation
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need the corporate CLA signed. If you have received this in error or have any questions, please contact us at [email protected]. Thanks! |
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks! |
Hey @kommander , thank you for the PR. I understand that it is valid JS to throw an arbitrary object. However in that case we should rather look into stringifying the object maybe? This will solve it more generally and not assume that |
@Neitsch Thanks for getting back to me on this. Do you mean stringifying as in just JSON.stringify it if it's not an Error instance? That could work. Not a nice message, but at least something to debug on. |
fa26adc
to
c5a139d
Compare
@Neitsch I tried with JSON.stringify and doesn't look too bad. I think I found a good place to test cover it as well. |
@IvanGoncharov - do you have thoughts here? I don't have any particular investment in this, but can't hurt if it helps Apollo? |
src/execution/execute.js
Outdated
return error; | ||
} | ||
if (error instanceof Object) { | ||
return new Error(JSON.stringify(error)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have special inspect
function for serializing objects to a string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your input. What are you using to inspect ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function called inspect
:)
It's already imported here:
graphql-js/src/execution/execute.js
Line 13 in 4abedd3
import inspect from '../jsutils/inspect'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hahaha, thanks for pointing that out 👍
src/execution/execute.js
Outdated
if (error instanceof Error) { | ||
return error; | ||
} | ||
if (error instanceof Object) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
error instanceof Object
doesn't handle some corner cases, e.g. Object.create(null)
better to user typeof error === 'object'
In general you shouldn't throw non-Error.
Note you can return instanceof
It should be definetly fixed in
This is citation from Even though I think it's incorrect behaviour to throw non-Error objects we should definetly handle it I propose to do the following: function asErrorInstance(error: mixed): Error {
if (error instanceof Error) {
return error;
}
return new Error('Unexpected error value: ' + inspect(error));
} |
Completely agree.
Most definitely agreed as well. I think this might have uncovered something else... resolveType reject with a valid Error comes through as string. EDIT: I pushed the broken test so you can see whats happening. I couldn't find a lead right away. |
@@ -393,6 +397,10 @@ describe('Execute: Handles basic execution tasks', () => { | |||
// eslint-disable-next-line no-throw-literal | |||
throw 'Error getting syncRawError'; | |||
}, | |||
syncObjectError() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we don't distinguish between different types of non-Errors I don't think we need to add this test.
I think I found the mistake, the test itself was rejecting with a non-error... fixing. |
locations: [{ line: 10, column: 9 }], | ||
path: ['asyncRawReject'], | ||
}, | ||
{ | ||
message: '', | ||
message: 'Unexpected error value: undefined', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During troubleshooting, I think it would be really helpful to receive this error instead of the empty string 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Yes! Just had a situation today again where the error was just empty... debugging something like that is hell.
I think travis had a problem with coverage reports. Tests are running fine. Maybe someone can retrigger it. |
It's my fault I broke the coveralls 😢 |
@kommander I think it's ready to merge.
I want to squash and merge this PR into a single commit but can't come up with a good commit message. Do you have any suggestions? |
@kommander Something that describes PR content instead of original intention behind it. |
"Inspect non-error types to produce helpful error messages for failing resolvers (even though you should not throw literals, ever)"? Maybe drop the last part. |
@IvanGoncharov Thanks, may Dijkstra be with you! |
@kommander Thanks for taking the time to submit PR and sorry for the long review. |
Hey @IvanGoncharov, do you know when this code change will be released? I tested it in the |
@KrisSiegel I'm working on it ⏳ |
Thanks. Yeah, I'm currently doing that but targeting a specific commit hash so my build doesn't change each time CI kicks off :D |
This is a proposal to handle previously serialised error objects according to section 7.1.2 of the spec. The result of receiving an error as object, without being an instance of the Error type, is an error message of
[object Object]
. The serialisation problem can be observed when working with remote executable schemas in Apollo GraphQL Tools, when throwing an Error in a remote resolver, it bubbles up as deserialised GraphQL Error according to the spec, but is not an Error instance.A local resolver can also accidentally throw an Object/literal, which is valid JS,
butand an error none-the-less.My take is that GraphQL should handle valid error Objects, as the spec states:
This may be fixed in graphql-tools, but I guess this is haunting other implementations as well. I'd event go as far as to deserialise the original stack trace.
I'll try to find a fix for remote executable schemas in apollograqphql/graphql-tools in the meantime, which could be a correct deserialisation in delegateToSchema.
EDIT: Heres a reproduction with apollo, resulting in an
[object Object]
error message in both cases https://github.com/kommander/graphql-bug