Apollo-client: Error is not retrieved from the cache

Created on 14 Nov 2018  路  37Comments  路  Source: apollographql/apollo-client

I have been trying to implement a standard UI where a particular resource can be found on a path like this /user/:id.
If I try to access a user that I don't have permissions to I expect server to return an error and a user node to be null.
However if I try to access the same resource again, user will be retrieved from the cache even though it's null but error field is undefined - that prevents me from reacting to the data correctly as I don't know on the second pass whether null means an error and what kind of error since error field is not present. The first request gives me correctly user and error.

Is it possible to setup cache policy to either not store null resources when errors are present or make sure that errors are cached as well?

My Setup:

const cache = new InMemoryCache();
const defaultOptions = {
    watchQuery: {
        fetchPolicy: 'cache-first',
        errorPolicy: 'all'
    },
    query: {
        fetchPolicy: 'cache-first',
        errorPolicy: 'all'
    },
    mutate: {
        errorPolicy: 'all'
    }
};
const client = new ApolloClient({
    shouldBatch: true,
    link: ApolloLink.from([new HttpLink({ uri: GRAPHQL_ENDPOINT })]),
    cache,
    defaultOptions
});

Thank you!

Most helpful comment

It would be nice for this issue to be looked into or for the docs to be updated to remove the false claims of what this repository is able to achieve.

I understand this is a bug and should be resolved rather than being removed from the documentation, but this has not been looked into for 8 months and the documentation has not been amended so people are evidently getting confused over it.

I just wasted several days work because I assumed this would work. Now I have had to delete much of my recent work and build my own custom store as a hacky way of storing errors until this is fixed.

All 37 comments

Facing the same issue currently, we're using graphql-shield which throws an error when a permission check isn't met. Apollo caches the result as null but doesn't cache the error.

In the docs it mentions that errors should be cached, so I'm guessing this used to work.
"it saves both data and errors into the Apollo Cache so your UI can use them."

Also it looks like deafultOptions aren't respected anyway #2555, #3256, however setting errorPolicy: 'all' on the query works, just a pain to set it on every query that may throw an error.

You can make this work by using the following, however it's just skipping the cache and ignoring the issue entirely.

errorPolicy: 'all',
fetchPolicy: 'no-cache',

We have the same issue. Error is undefined on second attempt - component tries to render null data and crashes. Used fetchPolicy: 'no-cache' workaround for now.

Same issue. We have a query that we don't want to throw an exception during a server side render. Setting the errorPolicy to 'all' solves this, but the error is never sent to the client's Query component. Setting the fetchPolicy to no-cache does work, but it also causes the query to be re-issued.

Same issue here. We are attempting to let users refetch on a per query basis when there are errors. Second time the query runs (if pulling from the cache) no errors are shown and we just get the cached data.

Any updates on this? The workaround obviously defeats the purpose of caching.

Any updates on this? The workaround obviously defeats the purpose of caching.

@joepuzzo I have fixed this in react-apollo. See change:
https://github.com/apollographql/react-apollo/commit/9d2eac96b724300d68163d464ad583f33456265e#diff-f8938b597e7a7647b9ea5589d55d3a26

You can use 'errorPolicy' of 'all' now, and the Query component will be issued the error during a SSR. This, of course, assumes you are working with Query component and does not affect the usage of client.query.

Ok awesome! So it looks like i just need to pull latest release then

Hmm just tried it and im using error policy all. Still not loading the error from cache

Hmm just tried it and im using error policy all. Still not loading the error from cache

Are you using apollo-react's Query component? It only works with that. If using client.query, then it won't work. I updated my earlier response with additional requirements around it. Query component is the only work around. Considering the re-design efforts involved in fixing it in the client cache, I personally wouldn't expect it to be fixed anytime soon or at all, but that's just my 2 cents.

Yup im using import { Query } from 'react-apollo'; with <Query errorPolicy="all"

And also NOOO thats really unfortunate because i started moving away from render props and the Query component completely and use a useQuery hook instead 馃槩

But regardless strange that im not seeing the fix when using the Query comp 馃

Hmm just tried it and im using error policy all. Still not loading the error from cache

Ensure your errorPolicy is null. Then during the server side render, you should see the error prop be populated with your error, rather than it throwing an exception. Depending on your fetch-policy, you may need to set something like ssrForceFetchDelay: 1000 on your ApolloClient as well to prevent it from re-issueing the request on the client during hydration (catch-and-network policy for example).

Im a little confused. Should i set my error policy to "all" or null?

Currently my error policy is "all" which is what i need / would expect. Whats happening is the first request gets the error and data: null when i hit back and then go back to the page it simply pulls data: null from the cache as the original issue describes.

Currently my error policy is "all" which is what i need / would expect. Whats happening is the first request gets the error and data: null when i hit back and then go back to the page it simply pulls data: null from the cache as the original issue describes.

That would be expected behavior depending on how your schema is setup. When you have errorPolicy set to all, it will try and return any usable response data that matches your schema response type. In this case, your schema type is nullable, so it caches null because it errored in a way which prevented it from converting any data to match the schema definition. If you do not want null to be returned/cached then you must make your response non-nullable.

Problem is that if I were to make the response non nullable then I cant get errors and data returned cleanly. The status code returned turns into a 500 by express-graphql and then apollo will treat it differently due to the non 200 status code.

To elaborate, the reason i choose to put nullable field on the schema is because when i make it non nullable the server will return 500 status code. When this happens, apollo client will say "Oh no a non 200 status code, well i better ignore the info that came back in the response and just let the user know there was a network error". Unless this is another bug in apollo client idk what to do.

Looks like there has been issue about this ( non nullable vs nullable field ) for over a year https://github.com/apollographql/apollo-client/issues/3034 ...

So it looks like this was an issue and closed... very confused as to why it was closed as no clear solution was found for the original issue. https://github.com/apollographql/react-apollo/issues/1781

Is there any progress on this? From my understanding of the docs:

{
  error: {
    graphqlErrors: {
      path: 'foo',
      ...
    }
  },
  data: {
    foo: null,
    bar: 123,
  }
}

should continue to pull that error from the cache and return it to the child renderer as long as errorPolicy: 'all' is set. In practice the error object is dropped entirely whenever this data is read from the cache. If data.foo becomes non-nullable this error becomes fatal on the server, which is not the desired behavior.

It would be nice for this issue to be looked into or for the docs to be updated to remove the false claims of what this repository is able to achieve.

I understand this is a bug and should be resolved rather than being removed from the documentation, but this has not been looked into for 8 months and the documentation has not been amended so people are evidently getting confused over it.

I just wasted several days work because I assumed this would work. Now I have had to delete much of my recent work and build my own custom store as a hacky way of storing errors until this is fixed.

@Thomas-Smyth Can you OS this custom store?

@Thomas-Smyth Can you OS this custom store?

I ended up going around it a different way in the end.

@Thomas-Smyth Can you OS this custom store?

I ended up going around it a different way in the end.

@Thomas-Smyth what did you do?

@okikejiani I had planned to use error to return _"You do not have permission to view ..."_. I would then get info from the returned error to find what aspect of the UI could not be rendered and then replace it with a _"No permission"_ message.

When reloading the page the cache would get the data, but not the error so I would not know what to replace with a _"No permission"_ message.

I got around it by just querying my API to see what they have permission to view as a separate part of the query and then using that to determine what parts of the UI should be replaced with a _"No Permission"_ message.

Any updates on this? The problem seems quite major to me.

Hi, we are still facing similar. We had a workaround to refetch programmatically when we suspect an error occurred. This workaround stopped working when upgraded from ^2.5.5 to ^3.1.0. However, an additional consecutive refetch (refetch().then(refetch)) solved the problem, but it's quite annoying.

Is there any ongoing effort to fix this problem?

Even if this isn't resolved, is it possible to get an answer from the team? I mean, even recommendations on workarounds (apart from no-cache) will do just fine.

Steps to reproduce in a React environment:

  1. Create a gql query that results into an error (for example, by explicitly returning an error from your server). Let's name it MY_GQL_QUERY.

  2. Create a basic React app that toggles between two components while using the query defined above:

import React from 'react;

const Component = ({ toggleComponent }) => {
const { loading, error } = useQuery(MY_GQL_QUERY, { fetchPolicy: 'cache-first' });

 return (
    <div>
        {loading && 'Loading query...'}
        {error && error.graphQLErrors[0].message}
       <button onClick={toggleComponent}>Render the other component</button>
    </div>
 )
}

/* This is the main app */
const App = () => {
  const [isFirstComponentShown, showFirstComponent] = React.useState(true);
  const toggleComponent = () => showFirstComponent(!isFirstComponentShown);

  // renders the same component under a different key to emulate 2 different components
  return isFirstComponentShown ? (
     <Component key="first" toggleComponent={toggleComponent} />
   ) :  (
     <Component key="second" toggleComponent={toggleComponent} />
   )
}

  1. Initially you will see loading... followed by the error that you have created.

  2. Navigate to the other component (its clone) by clicking on the button

  3. Instead of the seeing the error, you should have a JS exception. This is because the error key is not properly persisted in the cache and resets to undefined instead of keeping the value that received from the server.

I am using useQuery . I set errorPolicy: "all" . I have 2 queries in a query . Eg .
const GET_MULTI_QUERY = gql query GetMulti { ldapGroups(email: "myemail") listQueues(request: {}) { queues { uuid version name } } } ;

Now if one of them errors out , I expect to get data for the other one as well as get the error with what has been said in the documentation . However, I am not getting the error . I get error as undefined . I tried errorPolicy: 'none' , this sets data to null and only gives the error . Also, errorPolicy: 'ignore' does not work . I am using SSR but that should not matter .

This is a long thread and I guess the expectation is get the error as well as the data possible .

Any progress or anyone working on this ? This looks like a very basic requirement as the playgroung even works like that .

I am using useQuery . I set errorPolicy: "all" . I have 2 queries in a query . Eg .
const GET_MULTI_QUERY = gql query GetMulti { ldapGroups(email: "myemail") listQueues(request: {}) { queues { uuid version name } } } ;

Now if one of them errors out , I expect to get data for the other one as well as get the error with what has been said in the documentation . However, I am not getting the error . I get error as undefined . I tried errorPolicy: 'none' , this sets data to null and only gives the error . Also, errorPolicy: 'ignore' does not work . I am using SSR but that should not matter .

This is a long thread and I guess the expectation is get the error as well as the data possible .

Any progress or anyone working on this ? This looks like a very basic requirement as the playgroung even works like that .

I got the same issue, even I set errorPolicy: 'all', data.error has error for the first time, but after I went to another page and came back, the data.error became undefined. According to the doc, error should be cached in apollo cache, but it didn't.

+1, reproducible on apollo@3
any updates on this?

in the default options delete mutate: { errorPolicy: 'all' } worked for me , have it as apollo.create({ link, cache: new InMemoryCache(), defaultOptions: { watchQuery: { fetchPolicy: "cache-and-network", errorPolicy: "ignore", }, query: { fetchPolicy: "network-only", errorPolicy: "all", }, }, });

errorPolicy: 'all',
Doesn't work at all. No error info saved to the Cache :(

Here are some relevant findings and repro steps proving that apollo's own two recommended methods for SSR getDataFromTree() vs renderToStringWithData() produce different results because of the lack of error caching https://github.com/apollographql/apollo-client/issues/3897#issuecomment-729320646.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jamesreggio picture jamesreggio  路  3Comments

MichaelDeBoey picture MichaelDeBoey  路  3Comments

stubailo picture stubailo  路  3Comments

joergbaier picture joergbaier  路  3Comments

skeithtan picture skeithtan  路  3Comments