{"record":{"id":"ec3041004e9436f0","repo":"apify/crawlee","slug":"the-state-object-is-invalid-nextindex-must-be-a-n","errorCode":null,"errorMessage":"The state object is invalid: nextIndex must be a non-negative number.","messagePattern":"The state object is invalid: nextIndex must be a non-negative number\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/storages/request_list.ts","lineNumber":504,"sourceCode":"     * are automatically persisted at RequestList initialization (if the persistRequestsKey is set),\n     * but there's no reason to persist it again afterwards, because RequestList is immutable.\n     */\n    private async persistRequests(): Promise<void> {\n        const serializedRequests = await serializeArray(this.requests);\n        this.#store ??= await KeyValueStore.open();\n        await this.#store.setValue(this.#persistRequestsKey!, serializedRequests, { contentType: CONTENT_TYPE_BINARY });\n        this.areRequestsPersisted = true;\n    }\n\n    /**\n     * Restores RequestList state from a state object.\n     */\n    private restoreState(state?: RequestListState): void {\n        // If there's no state it means we've not persisted any (yet).\n        if (!state) return;\n        // Restore previous state.\n        if (typeof state.nextIndex !== 'number' || state.nextIndex < 0) {\n            throw new Error('The state object is invalid: nextIndex must be a non-negative number.');\n        }\n        if (state.nextIndex > this.requests.length) {\n            throw new Error('The state object is not consistent with RequestList, too few requests loaded.');\n        }\n        if (\n            state.nextIndex < this.requests.length &&\n            this.requests[state.nextIndex].uniqueKey !== state.nextUniqueKey\n        ) {\n            throw new Error(\n                'The state object is not consistent with RequestList the order of URLs seems to have changed.',\n            );\n        }\n\n        const deleteFromInProgress: string[] = [];\n        state.inProgress.forEach((uniqueKey) => {\n            const index = this.#uniqueKeyToIndex[uniqueKey];\n            if (typeof index !== 'number') {\n                throw new Error(","sourceCodeStart":486,"sourceCodeEnd":522,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/core/src/storages/request_list.ts#L486-L522","documentation":"When a persisted state is restored, nextIndex (the pointer into the request list) must be a valid non-negative number. A persisted state with a missing, non-numeric, or negative nextIndex is considered corrupt, and restoreState() throws during initialize().","triggerScenarios":"The KeyValueStore record under the persistStateKey contains malformed data — manually edited, written by a different/incompatible version, truncated, or null/absent fields after deserialization.","commonSituations":"Manual edits of the key-value store between runs; restoring state persisted by an older crawlee version with a different state schema; corrupted storage after a crashed write.","solutions":["Delete the corrupted persisted state record (the CRAWLEE_<persistStateKey> entry in the default KeyValueStore) so a fresh state is created","Verify the state was written by the same crawlee version; migrate or clear on version upgrade","Wrap initialization to detect this error and fall back to starting without persisted state"],"exampleFix":"// before\n// corrupted state { nextIndex: -1 } restored from KV store\n// after\nconst store = await KeyValueStore.open();\nawait store.setValue('CRAWLEE_list-state', null); // drop corrupted state, list restarts fresh","handlingStrategy":"validation","validationCode":"function isValidState(state) {\n  return state == null || (typeof state.nextIndex === 'number' && Number.isInteger(state.nextIndex) && state.nextIndex >= 0);\n}\nconst stored = await store.getValue('CRAWLEE_list-state');\nif (!isValidState(stored)) await store.setValue('CRAWLEE_list-state', null); // drop corrupt state before open","typeGuard":"const isValidRequestListState = (s) => s == null || (typeof s?.nextIndex === 'number' && s.nextIndex >= 0);","tryCatchPattern":"try {\n  const list = await RequestList.open('list', { sources, persistStateKey: 'state' });\n} catch (e) {\n  if (e.message.includes('nextIndex must be a non-negative number')) {\n    const store = await KeyValueStore.open();\n    await store.setValue('CRAWLEE_state', null); // reset and retry once\n    return RequestList.open('list', { sources, persistStateKey: 'state' });\n  }\n  throw e;\n}","preventionTips":["Never hand-edit persisted state records","Clear persisted state when upgrading crawlee versions","Keep persistStateKey stable and unique per list"],"tags":["request-list","state","validation","corrupted-state"],"backgroundTag":"invalid-persisted-state","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}