Diagnosing Stale or Missing Data in Apollo Client InMemoryCache
When a mutation returns correct data but the UI shows stale or missing values, the cause is usually cache identity or typePolicies. Use this diagnostic guide to isolate the issue before adding refetches.
03 Aug 2026, 01:08 UTC

Your GraphQL response contains the new value, but the UI still shows the old one. Another screen updates immediately, a list duplicates an item, or a field reads null after a mutation. When the network tab shows a successful response with correct data, the problem is usually not the server—it is how Apollo Client stores that data in its InMemoryCache.
The useful takeaway: before adding refetchQueries or update functions, verify cache identity. Every shared entity needs __typename and a stable id, and every collection needs explicit typePolicies. Fixing identity first prevents most stale-read and duplicate-entry bugs.
Recognizable Condition
After a mutation, subscription, or refetch, you may see one of these patterns:
- One component shows old data while another updates.
- A field is unexpectedly
null. - The same entity appears twice in a list.
In each case there is usually no network error, and the GraphQL response itself contains the new value.
Likely Causes and Diagnostic Table
InMemoryCache normalizes responses by storing entities in a flat lookup table keyed by __typename and id. When that identity is unstable or missing, the cache cannot deduplicate or update objects correctly.
| Condition | Likely cause | Diagnostic check |
|---|---|---|
| Stale shared object after mutation | Missing __typename or unstable id | Inspect raw response and normalized cache keys in Apollo DevTools |
| Only one screen updates | Object is embedded or non-normalized | Look for generated cache ids or keyFields:false |
| List drops or duplicates pages | Missing keyArgs, merge, or read | Inspect typePolicies for the Query field |
| Interface/union fields read null | Missing possibleTypes or __typename in fragments | Compare schema and selections |
Ordered Checks Before Changing Configuration
Run these checks in order. They isolate whether the server response or the client cache is at fault.
- Reproduce with Apollo DevTools. Open the Apollo DevTools browser extension, select the affected operation, and capture the operation variables, request, and response. Confirm the response body contains the expected value.
- Confirm identity fields. Every shared entity in the response must include
__typenameplus a stableidor a documented business key. If a fragment omits__typename, normalization and fragment matching fail. - Inspect normalized cache keys. In DevTools, look at the cache entries for the affected type. Duplicate entries or generated ids such as
Product:auto-1indicate missing or non-unique identity. - Review
typePolicies. Check theInMemoryCacheconfiguration for the affected types and the parentQueryfields. MissingkeyArgsormergefunctions on paginated lists are common. - Test with
network-onlyorno-cache. Temporarily set the affected query tonetwork-onlyorno-cache. If the data becomes correct, the server is fine and cache identity or policy is implicated.
To confirm your package version, run npm ls @apollo/client in your project root. Cache defaults and typePolicies behavior are version-sensitive.
Fixes Tied to Findings
Missing __typename
Add __typename to selection sets for normalized objects. Do not alias or omit it; Apollo Client relies on it for cache identity and fragment matching.
Missing or Non-Unique Ids
If the server cannot return a unique id, configure keyFields with a stable business key. Use keyFields:false only for embedded value objects that are never shared across screens.
Paginated Lists
For cursor- or offset-based pagination, define keyArgs so different pages are cached separately, and provide a merge function that concatenates existing and incoming arrays instead of replacing them. If your UI reads the full list, add a read function that returns the concatenated result.
Interfaces and Unions
Supply possibleTypes generated from your schema so Apollo Client can resolve concrete types for interface and union fields.
Refetches and Updates
Use refetchQueries, awaitRefetchQueries, or the update function only after cache identity is correct. They can mask cache defects and increase network load.
Example Cache Configuration
In your client cache module (for example, src/cache.ts), a simplified policy for a paginated feed with a custom key might look like this:
import { InMemoryCache } from '@apollo/client';
const cache = new InMemoryCache({
typePolicies: {
// Use only when the server cannot return a unique id
Product: {
keyFields: ['sku'],
},
Query: {
fields: {
products: {
// Cache pages separately by category and page size
keyArgs: ['category', 'first'],
merge(existing = [], incoming, { args }) {
if (!args || !args.after) {
return incoming;
}
return [...existing, ...incoming];
},
read(existing) {
return existing;
},
},
},
},
},
});
export default cache;
Production merge functions must handle undefined existing or incoming values and pagination arguments, or they can drop or duplicate list items.
Reverting Changes
These fixes live in your client bundle. If a policy change introduces regressions, revert the cache module or remove the typePolicies override and rebuild. No server rollback is required, but clear the client cache in DevTools or restart the application to drop previously stored malformed entries.
Practical Engineering Decision
Treat cache identity as part of your API contract. Require __typename and a stable id or documented keyFields for entities shared across screens. Use keyFields:false only for value objects embedded in a single parent. Centralize typePolicies in one cache module, and add unit tests for read and merge behavior. Do not use refetchQueries as a substitute for correct normalization.
Escalation Criteria
- Schema or API owners: escalate if the server cannot provide stable ids,
__typename, or consistent identity semantics. - Minimal reproduction: escalate with a minimal reproduction if duplicate or corrupt cache entries persist after identity and
typePoliciesare verified. - Incident: treat as an incident if stale data affects authorization, billing, inventory, or other safety-critical values.
When escalating, include the @apollo/client version, platform, operation name and variables, response shape, cache configuration, and reproduction steps.
Verification and Limitations
To verify a fix, reproduce the flow in a scratch application: query an entity, mutate it, and inspect Apollo DevTools before and after. Confirm there is one normalized entry and that its fields update. Temporarily setting the query to network-only or no-cache isolates cache issues from server issues. Unit-test typePolicies read and merge functions with existing and incoming arrays plus pagination arguments, and assert concatenation, deduplication, and keyArgs behavior.
Apollo Router or Apollo Server composition, authentication, and network errors can look like client cache staleness. Always verify the response before changing cache configuration. Record @apollo/client, server/router, and schema versions when escalating, because cache defaults and composition behavior are version-sensitive.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.