TanStack/query · warning

Provided storage does not implement `entries` method. Garbag

Error message

Provided storage does not implement `entries` method. Garbage collection is not possible without ability to iterate over storage items.

What it means

Dev-only error from `persisterGc` in `createPersister` (`@tanstack/query-persist-client-core`). Garbage collection iterates every storage entry to find expired items; if the supplied `storage` lacks an `entries()` method this is impossible, so in development the persister fails loudly. In production it silently skips GC.

Source

Thrown at packages/query-persist-client-core/src/createPersister.ts:267

    if (storage?.entries) {
      const storageKeyPrefix = `${prefix}-`
      const entries = await storage.entries()
      for (const [key, value] of entries) {
        if (key.startsWith(storageKeyPrefix)) {
          let persistedQuery: PersistedQuery
          try {
            persistedQuery = await deserialize(value)
          } catch {
            await storage.removeItem(key)
            continue
          }
          if (isExpiredOrBusted(persistedQuery)) {
            await storage.removeItem(key)
          }
        }
      }
    } else if (process.env.NODE_ENV === 'development') {
      throw new Error(
        'Provided storage does not implement `entries` method. Garbage collection is not possible without ability to iterate over storage items.',
      )
    }
  }

  async function restoreQueries(
    queryClient: QueryClient,
    filters: Pick<QueryFilters, 'queryKey' | 'exact'> = {},
  ): Promise<void> {
    const { exact, queryKey } = filters

    if (storage?.entries) {
      const storageKeyPrefix = `${prefix}-`
      const entries = await storage.entries()
      for (const [key, value] of entries) {
        if (key.startsWith(storageKeyPrefix)) {
          let persistedQuery: PersistedQuery
          try {

View on GitHub (pinned to 159982c80b)

Solutions

  1. Implement `entries()` on your storage adapter, returning an iterable of `[key, value]` pairs (async iterator or array).
  2. Use a storage that already supports it, e.g. a `Map` or a fully-featured `localforage` instance.
  3. Wrap the storage: `const fullStorage = { ...storage, entries: async () => { ... } }`.
  4. If iteration is genuinely impossible, suppress GC by only calling `persisterGc` in environments where the storage supports it (the error is dev-only, so production will no-op).

Example fix

// before
const persister = createPersister({ storage: window.localStorage, ... })
// after
const storage = {
  ...window.localStorage,
  entries: async () => Object.entries(window.localStorage).map(([k, v]) => [k, v]),
}
const persister = createPersister({ storage, ... })
Defensive patterns

Strategy: validation

Validate before calling

function assertStorageIterable(storage: any) {
  if (!storage || typeof storage.entries !== 'function') {
    throw new Error('persisterGc requires storage.entries()')
  }
}

Type guard

const hasEntries = (s: any): s is { entries(): Promise<Iterable<[string, any]>> } =>
  !!s && typeof s.entries === 'function'

Try / catch

try { await persister.persisterGc() } catch (e) { if (/entries method/.test((e as Error).message)) console.warn('GC skipped:', e.message) }

Prevention

When it happens

Trigger: Passing a minimal storage shim (only `getItem`/`setItem`/`removeItem`) to `createPersister({ storage })`; using `localStorage`-like polyfills that omit `entries`; providing a custom AsyncStorage that did not implement iteration; calling `persisterGc()` directly in a dev build with such a storage.

Common situations: React Native `AsyncStorage` (no `entries`); an in-memory Map wrapper without exposing `entries`; older `localforage` versions; partial storage adapters written for a different interface.

Related errors


AI-assisted analysis of TanStack/query@159982c80b (2026-08-12). Data as JSON: /api/errors/4a93e840d7bf26c5. Report an issue: GitHub.