{"record":{"id":"70faf22f2c7bd11b","repo":"slopus/happy","slug":"codex-rewind-point-itemid-not-found-in-thread","errorCode":null,"errorMessage":"Codex rewind point ${itemId} not found in thread ${threadId}","messagePattern":"Codex rewind point (.+?) not found in thread (.+?)","errorType":"exception","errorClass":"CodexForkRewindPointNotFoundError","httpStatus":null,"severity":"error","filePath":"packages/happy-cli/src/codex/codexThreadFork.ts","lineNumber":120,"sourceCode":"        model?: string;\n        approvalPolicy?: any;\n        sandbox?: any;\n        mcpServers?: Record<string, unknown>;\n    },\n): Promise<CodexForkResult> {\n    const forked = await client.forkThread({\n        threadId: opts.threadId,\n        ...(opts.cwd ? { cwd: opts.cwd } : {}),\n        ...(opts.model ? { model: opts.model } : {}),\n        ...(opts.approvalPolicy ? { approvalPolicy: opts.approvalPolicy } : {}),\n        ...(opts.sandbox ? { sandbox: opts.sandbox } : {}),\n        ...(opts.mcpServers ? { mcpServers: opts.mcpServers } : {}),\n    });\n\n    if (opts.cutAfterItemId) {\n        const cutTurn = findCutTurn(forked.thread, opts.cutAfterItemId);\n        if (!cutTurn) {\n            throw new CodexForkRewindPointNotFoundError(opts.cutAfterItemId, opts.threadId);\n        }\n        const turns = forked.thread.turns ?? [];\n        const turnsToDrop = turns.length - cutTurn.index;\n        if (turnsToDrop > 0) {\n            await client.rollbackThread({\n                threadId: forked.threadId,\n                numTurns: turnsToDrop,\n            });\n        }\n        await client.injectItems({\n            threadId: forked.threadId,\n            items: [{\n                type: 'message',\n                role: 'user',\n                content: [{ type: 'input_text', text: cutTurn.text }],\n            }],\n        });\n    }","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/codex/codexThreadFork.ts#L102-L138","documentation":"forkCodexThread() throws CodexForkRewindPointNotFoundError when a cutAfterItemId is supplied but findCutTurn() cannot locate an item with that id among the forked thread's turns (it only matches items with user text). The rewind point is stale or belongs to a different thread.","triggerScenarios":"Calling forkCodexThread({ threadId, cutAfterItemId }) where the item id does not exist in the thread's turns, or exists as a non-user item; ids come from the mobile app RPC `result`/setRPCHandlers and may reference an older/rewritten thread.","commonSituations":"Rewinding after the thread was rolled back previously (id points past the cut); resuming under a new thread id where item ids changed; typo'd or truncated item id from the client UI; forking a thread whose turns failed to load.","solutions":["Refresh rewind points with listCodexRewindPoints(thread) and use a current item id","Verify cutAfterItemId comes from the same threadId being forked","Omit cutAfterItemId to fork the full thread when rewind isn't essential","Catch CodexForkRewindPointNotFoundError in the RPC handler and return the valid rewind-point list to the caller"],"exampleFix":"// before\nawait forkCodexThread({ client, threadId, cutAfterItemId: staleId }); // throws\n\n// after\nconst points = listCodexRewindPoints(thread);\nconst id = points.find(p => p.itemId === staleId)?.itemId ?? points.at(-1)?.itemId;\nawait forkCodexThread({ client, threadId, cutAfterItemId: id });","handlingStrategy":"validation","validationCode":"import { listCodexRewindPoints } from '@/codex/codexThreadFork';\nconst validIds = new Set(listCodexRewindPoints(thread).map(p => p.itemId));\nif (!validIds.has(cutAfterItemId)) {\n  throw new Error(`Unknown rewind point ${cutAfterItemId}; pick one of listCodexRewindPoints()`);\n}","typeGuard":"function isValidRewindPoint(thread: { turns?: Array<{ items?: Array<{ id?: string }> }> }, itemId: string): boolean {\n  return (thread.turns ?? []).some(t => (t.items ?? []).some(i => i.id === itemId));\n}","tryCatchPattern":"try {\n  await forkCodexThread({ client, threadId, cutAfterItemId });\n} catch (error) {\n  if (error instanceof CodexForkRewindPointNotFoundError) {\n    const points = listCodexRewindPoints(thread);\n    // respond to RPC caller with refreshed rewind points\n  } else { throw error; }\n}","preventionTips":["Always source cutAfterItemId from a fresh listCodexRewindPoints() call, never cached UI state","Re-fetch rewind points after any rollback or resume","Match threadId between the rewind point source and the fork call"],"tags":["codex","fork","rewind","invalid-argument"],"backgroundTag":"rewind-point-not-found","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}