{"record":{"id":"d122d907ededf545","repo":"paperclipai/paperclip","slug":"codex-history-unavailable","errorCode":"codex_history_unavailable","errorMessage":"codex_history_unavailable: requested turn is outside the retained runner event window","messagePattern":"codex_history_unavailable: requested turn is outside the retained runner event window","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/live/runnerd-codex-transport.ts","lineNumber":3356,"sourceCode":"          ...(correlationId ? { correlationId } : {}),\n        },\n        correlationId,\n      );\n      return {};\n    }\n    if (method === \"turn/interrupt\") {\n      await this.#command(\"turn.interrupt\", params);\n      return {};\n    }\n    if (method === \"thread/turns/list\" || method === \"thread/items/list\") {\n      if (params.threadId !== this.#threadId) throw new Error(\"codex_history_identity_mismatch\");\n      const snapshot = await this.request(\"thread/read\", { threadId: this.#threadId, includeTurns: false });\n      const turns = record(snapshot.thread).turns as Array<Record<string, unknown>>;\n      let data: Array<Record<string, unknown>>;\n      if (method === \"thread/turns/list\") {\n        data = turns.map(turn => ({ ...turn, items: [], itemsView: \"notLoaded\" }));\n      } else {\n        if (params.turnId !== this.#turnId) throw new Error(\"codex_history_unavailable: requested turn is outside the retained runner event window\");\n        const items = new Map<string, Record<string, unknown>>();\n        let observedTurn = \"\";\n        let observedStart = false;\n        for (const event of this.#core?.store.state.committedEvents ?? []) {\n          const payload = record(record(event.envelope.payload).payload);\n          if (event.eventType === \"turn.started\") observedTurn = String(payload.providerTurnId ?? payload.turnId ?? record(payload.turn).id ?? \"\");\n          if (event.eventType === \"turn.started\" && observedTurn === params.turnId) observedStart = true;\n          if (event.eventType !== \"item.completed\" || observedTurn !== params.turnId) continue;\n          const item = record(rehydrateRunnerdItemNotification(payload, this.#threadId, observedTurn).item);\n          if (typeof item.id === \"string\") items.set(item.id, { turnId: observedTurn, item });\n        }\n        if (!observedStart) throw new Error(\"codex_history_incomplete: requested turn start is outside the retained runner event window\");\n        data = [...items.values()];\n      }\n      if (params.sortDirection === \"desc\") data.reverse();\n      const offset = params.cursor == null ? 0 : Number(params.cursor);\n      if (!Number.isSafeInteger(offset) || offset < 0 || offset > data.length) throw new Error(\"codex_history_invalid_cursor\");\n      const limit = typeof params.limit === \"number\" ? Math.max(1, Math.min(100, params.limit)) : 100;","sourceCodeStart":3338,"sourceCodeEnd":3374,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/live/runnerd-codex-transport.ts#L3338-L3374","documentation":"For thread/items/list, the transport reconstructs items from the runner's retained committed-event window. If the requested turnId was never observed as started within that window (params.turnId !== this.#turnId), the history cannot be reconstructed and the transport throws instead of returning partial/truncated data.","triggerScenarios":"Calling request('thread/items/list', { threadId: <current>, turnId: X }) where X is not the transport's current #turnId — a turn from before the retained window or a future/unknown turn.","commonSituations":"Paging back through history past the retention window; requesting items for a turn that ran under a previous runner process; a client resuming after restart with an outdated turnId.","solutions":["Request the current turn id (via thread/read with includeTurns) and list items for that turn.","Use the durable persistence layer (state directory / database) for turns outside the retained event window.","Start a new turn before listing items; items only exist for turns the live runner has observed.","Do not retry with the same turnId — the window cannot grow backwards."],"exampleFix":"// before\nawait transport.request('thread/items/list', { threadId, turnId: staleTurnId });\n// after\nconst snapshot = await transport.request('thread/read', { threadId, includeTurns: true });\nconst currentTurnId = snapshot.thread.turns.at(-1).id;\nawait transport.request('thread/items/list', { threadId, turnId: currentTurnId });","handlingStrategy":"fallback","validationCode":"const turns = (await transport.request('thread/read', { threadId, includeTurns: true })).thread.turns;\nif (!turns.some(t => t.id === turnId)) throw new Error('turn not in retained window; use durable store');","typeGuard":null,"tryCatchPattern":"try {\n  items = await transport.request('thread/items/list', { threadId, turnId });\n} catch (err) {\n  if ((err as Error).message.startsWith('codex_history_unavailable')) {\n    items = await loadItemsFromDurableStore(threadId, turnId);\n  } else throw err;\n}","preventionTips":["Only request items for the current turn of a live transport","Persist turns durably if history beyond the live window is needed","Inform users when history is truncated by retention"],"tags":["history","retention","turn-window"],"backgroundTag":"record-not-found","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}