apollo-client
apollo-client copied to clipboard
"GraphQLErrors" field will often not populate, errors instead stuck in "NetworkError"
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
datafield of the response is empty, the GraphQL error is sent to the"NetworkErrors"field. - If the
datafield of the response is nonempty, the GraphQL error is sent to the"GraphQLErrors"field.
On 400 errors:
- If the
datafield of the response is empty, we lose the GraphQL error entirely. - If the
datafield of the response is nonempty, we get the double-render we see in https://github.com/apollographql/apollo-client/issues/8157.
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
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.
Thanks for opening this @MrDoomBringer! Believe we can include https://github.com/apollographql/apollo-client/issues/8010 in the list of related issues.
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...
@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.
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.
There's always a way...
Since:
err.networkError.result.errorscontains in fact graphQL errors- in an error link, you can alter the
networkErrormessage 😉
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:
- https://github.com/apollographql/apollo-client/issues/6222#issuecomment-731959890
- https://github.com/apollographql/apollo-link/issues/828#issuecomment-508865937
@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... 😉
The networkError object being passed to my onError has a statusCode of "undefined" for 401 errors:
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?