Skip to content
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

"GraphQLErrors" field will often not populate, errors instead stuck in "NetworkError" #9870

Open
MrDoomBringer opened this issue Jun 30, 2022 · 8 comments
Labels
🐞 bug 🔍 investigate Investigate further
Milestone

Comments

@MrDoomBringer
Copy link
Contributor

MrDoomBringer commented Jun 30, 2022

Intended outcome:

When a server sends a GraphQL Error (defined by the docs here) to Apollo Client, it is expected that ApolloClient will format the result to include this error in the "GraphQLErrors" part of the response, and put any network errors in the "NetworkError" field.

Actual outcome:

On 200 errors:

  • If the data field of the response is empty, the GraphQL error is sent to the "NetworkErrors" field.
  • If the data field of the response is nonempty, the GraphQL error is sent to the "GraphQLErrors" field.

On 400 errors:

Intended behavior or not, this is confusing for users and needs to change.

Errors that don't reach GraphQL execution (Validation, Parse, Bad-Input, etc) often result in no data being sent to the client, resulting in the behavior we see above.


How to reproduce the issue:

In the fullstack-tutorial, try placing this query in the GET_LAUNCH_DETAILS query at the top of final/client/src/pages/launch.tsx

  query LaunchDetails {
    launch(id: "foobar") {
      site
      ...LaunchTile
    }
  }
Response from the server {"errors":[{"message":"Cannot read properties of undefined (reading 'flight_number')","locations":[{"line":2,"column":3}],"path":["launch"],"extensions":{"code":"INTERNAL_SERVER_ERROR","exception":{"stacktrace":["TypeError: Cannot read properties of undefined (reading 'flight_number')"," at LaunchAPI.launchReducer (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\src\\datasources\\launch.js:12:18)"," at LaunchAPI.getLaunchById (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\src\\datasources\\launch.js:38:17)"]}}}],"data":{"launch":null}}
Result: JSON.stringify(error) ERROR: {"graphQLErrors":[{"message":"Cannot read properties of undefined (reading 'flight_number')","locations":[{"line":2,"column":3}],"path":["launch"],"extensions":{"code":"INTERNAL_SERVER_ERROR","exception":{"stacktrace":["TypeError: Cannot read properties of undefined (reading 'flight_number')"," at LaunchAPI.launchReducer (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\src\\datasources\\launch.js:12:18)"," at LaunchAPI.getLaunchById (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\src\\datasources\\launch.js:38:17)"," at processTicksAndRejections (node:internal/process/task_queues:96:5)"]}}}],"clientErrors":[],"networkError":null,"message":"Cannot read properties of undefined (reading 'flight_number')"}

Then try again, but with the following query:

  query LaunchDetails {
    launch(id: 1) {
      site
      foobar
      ...LaunchTile
    }
  }
Response from the server {"errors":[{"message":"Cannot query field \"foobar\" on type \"Launch\".","extensions":{"code":"GRAPHQL_VALIDATION_FAILED","exception":{"stacktrace":["GraphQLError: Cannot query field \"foobar\" on type \"Launch\"."," at Object.Field (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\validation\\rules\\FieldsOnCorrectTypeRule.js:48:31)"," at Object.enter (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\language\\visitor.js:323:29)"," at Object.enter (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\utilities\\TypeInfo.js:370:25)"," at visit (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\language\\visitor.js:243:26)"," at validate (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\validation\\validate.js:69:24)"," at validate (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\apollo-server-core\\dist\\requestPipeline.js:186:39)"," at processGraphQLRequest (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\apollo-server-core\\dist\\requestPipeline.js:98:34)"," at async processHTTPRequest (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\apollo-server-core\\dist\\runHttpQuery.js:220:30)"]}}}]}
Result: JSON.stringify(error) ERROR: {"graphQLErrors":[],"clientErrors":[],"networkError":{"name":"ServerError","response":{},"statusCode":400,"result":{"errors":[{"message":"Cannot query field \"foobar\" on type \"Launch\".","extensions":{"code":"GRAPHQL_VALIDATION_FAILED","exception":{"stacktrace":["GraphQLError: Cannot query field \"foobar\" on type \"Launch\"."," at Object.Field (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\validation\\rules\\FieldsOnCorrectTypeRule.js:48:31)"," at Object.enter (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\language\\visitor.js:323:29)"," at Object.enter (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\utilities\\TypeInfo.js:370:25)"," at visit (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\language\\visitor.js:243:26)"," at validate (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\graphql\\validation\\validate.js:69:24)"," at validate (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\apollo-server-core\\dist\\requestPipeline.js:186:39)"," at processGraphQLRequest (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\apollo-server-core\\dist\\requestPipeline.js:98:34)"," at async processHTTPRequest (E:\\ApolloGraphQL\\git\\fullstack-tutorial\\final\\server\\node_modules\\apollo-server-core\\dist\\runHttpQuery.js:220:30)"]}}}]}},"message":"Response not successful: Received status code 400"}

Notice how the first server response has a data field and populates the "GraphQLErrors" field, while the second response has no data and as such populates the "NetworkError" field.

The following are, to my understanding, directly related to this issue.
#6222
#8010
#8157
#8470
#8503
#9348

Versions

System:
OS: Windows 10 10.0.19043
Binaries:
Node: 17.6.0 - F:\Program Files\nodejs\node.EXE
Yarn: 1.22.19 - E:\ApolloGraphQL\git\fullstack-tutorial\final\client\node_modules.bin\yarn.CMD
npm: 8.5.1 - F:\Program Files\nodejs\npm.CMD
Browsers:
Edge: Spartan (44.19041.1266.0), Chromium (103.0.1264.37)
npmPackages:
@apollo/client: ^3.6.9 => 3.6.9
apollo: ^2.32.5 => 2.34.0

@MrDoomBringer MrDoomBringer self-assigned this Jun 30, 2022
@MrDoomBringer MrDoomBringer added this to the Release 4.0 milestone Jun 30, 2022
@MrDoomBringer
Copy link
Contributor Author

As far as resolving this goes: Drastically changing the way errors are reported will be a breaking issue, (certainly impacting tests at least), so for now we'll plan to save a true fix for ACv4.

In the meantime we'll be updating the docs here shortly to reduce confusion about this particular issue.

@alessbell
Copy link
Contributor

Thanks for opening this @MrDoomBringer! Believe we can include #8010 in the list of related issues.

@jnssnmrcs
Copy link

Any news on this? I'm trying to let the server do as much validation as possible with graphql-yoga and custom scalars. However the validation errors are being sent with http status 400 and thus gets lost when it reaches my react component so it's impossible to know which field failed validation and why. This forces me to duplicate the validation on the client side in order to show the user meaningful error messages...

@phryneas
Copy link
Member

@jnssnmrcs As MrDoomBringer already said above, this is nothing we can do in-between major releases. We are still targeting this for 4.0, but that will be a big release and we are still on the way to 3.8 right now.

@facundofernandez
Copy link

Hi, is there any progress on the issue? I am wanting to migrate from v2.x to v3.x, but these behaviors described earlier do not let me continue. My errors disappear when trying to capture them from the client.

@M1CK431
Copy link

M1CK431 commented Dec 2, 2023

There's always a way...

Since:

  • err.networkError.result.errors contains in fact graphQL errors
  • in an error link, you can alter the networkError message 😉
import { onError } from "@apollo/client/link/error";

const errorLink = onError(err => {
  if (err.networkError?.statusCode === 400)
    err.networkError.message = err.networkError.result.errors[0].message;
});

Credits
I found that workaround reading:

@M1CK431
Copy link

M1CK431 commented Dec 2, 2023

@MrDoomBringer

As far as resolving this goes: Drastically changing the way errors are reported will be a breaking issue

and

@phryneas

As MrDoomBringer already said above, this is nothing we can do in-between major releases.

I can't agree with you. You can still add an option that alter the error handling behavior. This way, only people which encounter that issue will use it. No breaking change between major version, semver respected, everyone happy 🎯 🏆

As I say in my previous comment, there is always a way... 😉

@john-hall
Copy link

The networkError object being passed to my onError has a statusCode of "undefined" for 401 errors:

image

I also tried using the operation.getContext but that also returns "undefined". Is the ANY way to detect a 401 status code using the apollo client for react?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐞 bug 🔍 investigate Investigate further
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants