useSuspenseQuery and query errors confusion

Hello!

I’m using useSuspenseQuery to suspend my render until the query is done. I’m confused about the fact that useSuspenseQuery seems to return an error (in addition to data). But, if the query fails, that error never seems to actually be returned to me. Instead, the error makes it to a higher-level <ErrorBoundary> component. This is the documented behavior, but I’m left to wonder what the point of the advertised error return is. Is it ever used?
Indeed, in one use case I would have preferred to get the error through this return - for one, because the ErrorBoundary doesn’t really get any information about which query failed out of possibly multiple.

Thanks!

I have discovered that, if I set errorPolicy: "all", I get the error in the error return value on query completion, instead of the error being thrown as an exception. This seems to be the case for useSuspenseQuery, as well as in other situations like client.query(). Are there any docs that explain this? I’m reading Handling operation errors - Apollo GraphQL Docs, and I don’t see mentions of throwing exceptions versus returning errors in relationship to the errorPolicy.

Thanks!

Hey @andreimatei :wave:

Check out the Suspense error handling docs for this documented behavior. There are two key sections that document the throw vs error property behavior. The first sentence in the error handling section states:

By default, both network errors and GraphQL errors are thrown by useSuspenseQuery . These errors are caught and displayed by the closest error boundary.

And this section on “Rendering partial data alongside errors” states:

In some cases, you may want to render partial data alongside an error. To do this, set the errorPolicy option to all . By setting this option, useSuspenseQuery avoids throwing the error and instead sets an error property returned by the hook. To ignore errors altogether, set the errorPolicy to ignore . See the errorPolicy documentation for more information.

Is there something that could be more clear on our end to help clarify this behavior?

Thanks, Jerel!

In retrospect, the docs on suspense error handling are indeed fine; thanks for spelling them out.
The docs on useQuery also seem fine.

One thing that threw me (ha!) though is the behavior of client.Query() (with client = useApolloClient()). As far as I can tell, client.Query()behaves likeuseSuspenseQuery(), respecting errorPolicyand throwing if the policy isNone, and returning the error otherwise. I did not see this documented. I am also a bit confused about the errorsvserrorfields inApolloQueryResult`; consider if there’s sufficient docs about them.


If you don’t mind, I also have a question about the interaction of errors and caching. It seems to me that error responses are cached (at least for “resolver errors”; not sure about network errors), and the cached data is returned by subsequent queries (at least when using the default fetchPolicy). Is there any way to avoid caching errors? I can’t find a discussion about this in the docs. In an old version of the docs I did find a note about how errors are cached if errorPolicy = "all" is used, and not otherwise. Is that still current? If so, is there still a way to get the error returning behavior of errorPolicy = "all", but not the error caching behavior?

Thanks!

As far as I can tell, client.Query()behaves like useSuspenseQuery(), respecting errorPolicyand throwing if the policy is None, and returning the error otherwise. I did not see this documented

Ah ya what you’re seeing here is the difference in our “core” API and the “React” API. client.query() is UI framework agnostic, and because it returns a promise which you need to await yourself, rejecting on errors here make sense. Totally agree with you though that our documentation could be better in this regard. The closest thing we have to spelling this out is the client.query() API doc, which states:

This resolves a single query according to the options specified and returns a Promise which is either resolved with the resulting data or rejected with an error.

This is hard to find though. At some point in the future, we’d like to revamp our docs to provide more documentation on our core API first, while moving some of the React bits later. Our docs make it seem like Apollo Client is a React library since it provides the basis of most of the client behavior through the React API, but this isn’t quite accurate as the core API provides pretty rich behavior on its own.

We’ll do our best to see if we can find some stop-gap solutions for now until we get our docs revamp, and this is definitely one of them. Sorry for the confusion here!

I am also a bit confused about the errorsvs errorfields in ApolloQueryResult`; consider if there’s sufficient docs about them.

To be honest, I’m not entirely sure of the history here and why this design choice was made. I believe errors is a shortcut to error.graphqlErrors, but its not entirely obvious to me why we can’t just ask our users to access this via the error field.

This is actually why useSuspenseQuery only exposes error (when you have the right errorPolicy set), to try and avoid this confusion. I’d love to see if we can remove the error/errors distinction and make this a little nicer in a future version, but we’ll need to do this in a major version as this would be a breaking change.

It seems to me that error responses are cached (at least for “resolver errors”; not sure about network errors)

Could you describe to me what you mean by “resolver errors” here?

Errors should not be cached at all by the client, so if you’re seeing this, there must be a bug somewhere. What version of Apollo Client are you using?