-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Incorrect behavior on error handling of a mutation with multiple root fields #573
Comments
I think you're right -- this is a bug! What's the return type of |
Also, can you tell me a bit more about how this error is handled? For example, what kind of error is it and what technique is used to catch that error in GraphQL (eg, |
The first mutation is raising a
The check_in mutation payload type is an union type accepting either one of: Let me know if you need any more info |
Thanks, i'll take a look! |
I've written up a little test on this branch and it seems to pass: https://github.com/rmosolgo/graphql-ruby/compare/mutation-error-handling Could you share some more info to help track this down? These would be helpful:
|
Your spec clearly indicated the issue is not in the gem but in our code, so I've spent some time debuggin the issue. This definition causes the error: field @mutation_name do
type !payload_type
end while this does not: field @mutation_name do
type payload_type
end Conceptually we thought that when a field yield errors it is not actually resolving to |
It seems like an error in null propagation, and a pretty serious one. GraphQL is executing fields, but not telling you! Are you willing to share your gem versions? |
I updated my branch to test non-null fields and it passed, so I wonder if it's a difference between gem versions or some other factor! |
Sorry I omitted the gem versions.
The resolver for the class Resolver < LetsGraph::MutationResolver
def resolve # called by superclass #call and used as return value to #call
# just forcing an error on the first mutation I was sending for testing purposes
return GraphQL::ExecutionError.new 'Buuu!' if input[:id] == 'fail'
@invitation = Invitation.find input[:id]
# .... perform check_in
@invitation
end
end I also tested with an inline resolve function, same results, so I do not believe the issue to be related to the resolve. Now the payload type is tricky, I'm autogenerating it from database columns (mapping db types to graphQL types). Looking at your specs, the fundamental definition I see compared with my mutations is that I'm not using GraphQL::Relay::Mutation. It is somewhat autogenerated but it would translate to something along these lines: GraphQL::ObjectType.define do
field :check_in do
type !Mutations::Invitations::CheckIn::PayloadType # Union of two common object types
argument :input, !Mutations::Invitations::CheckIn::InputType
resolve Mutations::Invitations::CheckIn::Resolver.new
end
end The generated "signature" for the mutation is Maybe the difference in using |
Thanks, that's really helpful. I think you're right about the difference being Just to confirm: |
Yes! |
Thanks for all the details! I've replicated the issue here: https://github.com/rmosolgo/graphql-ruby/tree/mutation-field-error-test If a
I thought that "Normal and Serial Execution" would specify that "execution halts if a field raises a field error", but I don't see such a specification. Do you? I've asked for clarification here: graphql/graphql-spec#277 |
I am under the impression this statement is assuming a single root field with a Non-null path to a child with errors... I'm more convinced that spec-wise the correct result should be
If we do accept the above interpretation, for consistency we have to consider that serial execution should halt. Executing further fields with side-effects while also denying the user the possibility of asserting its results would be extremely confusing. I also see a logical argument on the fact that enforcing serial execution for mutations does mean assuming dependency between the multiple root fields in the first place... I can think of quite a few use cases where halting the execution would be far more useful than allowing fields to execute independently. A possible way to support both use cases would be to allow execution of further fields if the field yielding the error is of nullable return type, but halting execution if it is not nullable. Doing otherwise would yield a response that violates the schema. I wonder if the client could have voice on this decision too. Finally:
😂 |
Agreed, here's a fix: #575 |
Thanks for your help tracking this down! It seems like others agree over at graphql/graphql-spec#277. This fix is released in |
Thanks to you, I'm actually glad to be of help. We'll upgrade right away! |
Hello,
once again, thanks for this amazing work. I think I'd run into a bug regarding error handling when executing multiple mutation fields on the same mutation. Please consider:
Now suppose the first field raises a not authorized
GraphQL::ExecutionError
exception. The api output is something along these lines:As you can see data is
null
, and hence no output is present forsecond_mutation
. However, after inspecting the system state, I can verify that the mutation has been executed and the side-effects caused by it still persist.In other words, the check_in operation of the second mutation executes successfully although the requester is unable to retrieve a response for it.
I believe the expected behavior should include the response for
second_mutation
underdata
,null
for the first mutation, while also exposing the error for the first mutation undererrors
.Reference: graphql specs (https://facebook.github.io/graphql/#sec-Errors-and-Non-Nullability),
Could you please confirm if my assumptions and interpretation of the specs are correct? If affirmative any chance we could have this fixed in upcoming releases?
The text was updated successfully, but these errors were encountered: