apify/crawlee · error · Error

Cannot persist state. options.persistStateKey is not set.

Error message

Cannot persist state. options.persistStateKey is not set.

What it means

persistState() saves the list's processed-position state to the default KeyValueStore, but only when a persistStateKey was configured at construction. Without the key there is nowhere to persist, so the method throws instead of silently doing nothing.

Source

Thrown at packages/core/src/storages/request_list.ts:458

                    // oxlint-disable-next-line typescript/no-array-delete -- intentional, drop the slot so V8 can collect the object
                    delete sourcesFromFunction[i];
                    this.addRequest(source);
                }

                sourcesFromFunction.length = 0;
            } catch (e) {
                const err = e as Error;
                throw new Error(`Loading requests with sourcesFunction failed.\nCause: ${err.message}`);
            }
        }
    }

    /**
     * @inheritDoc
     */
    async persistState(): Promise<void> {
        if (!this.#persistStateKey) {
            throw new Error('Cannot persist state. options.persistStateKey is not set.');
        }
        if (this.isStatePersisted) return;
        try {
            this.#store ??= await KeyValueStore.open();
            await this.#store.setValue(this.#persistStateKey, this.getState());
            this.isStatePersisted = true;
        } catch (e) {
            const err = e as Error;
            this.#log.exception(err, 'Attempted to persist state, but failed.');
        }
    }

    /**
     * Removes the `PERSIST_STATE` event listener registered during initialization and persists
     * the current state one last time. Call this when you are done with the `RequestList` to avoid
     * leaking the listener (and the requests it retains) on the shared event manager.
     */
    async teardown(): Promise<void> {

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Pass persistStateKey when creating the list: new RequestList({ sources, persistStateKey: 'my-state' })
  2. Or guard the call: only invoke persistState() when list has a configured key
  3. If state persistence is unwanted, remove the persistState() call rather than catching the error

Example fix

// before
const list = await RequestList.open('list', { sources });
await list.persistState(); // throws
// after
const list = await RequestList.open('list', { sources, persistStateKey: 'list-state', persistRequestsKey: 'list-reqs' });
await list.persistState();
Defensive patterns

Strategy: try-catch

Validate before calling

if (!list.persistStateKey && typeof list.persistState === 'function') {
  // configure the key at construction instead of calling persistState
}

Try / catch

try {
  await list.persistState();
} catch (e) {
  if (e.message.includes('persistStateKey is not set')) {
    console.warn('State persistence skipped: no persistStateKey configured');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling list.persistState() (directly or via crawler.teardown / persistence hooks) on a RequestList constructed without a persistStateKey option.

Common situations: Adding periodic persistence to an existing list and forgetting the constructor option; calling crawlerTeardown/persist hooks generically for all storages while one list lacks the key.

Understand the failure class

Background: "Must pass :limit option" / "Missing required option" — required option errors explained — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30). Data as JSON: /api/errors/f9291cbb09fa5390. Report an issue: GitHub.