slopus/happy · error · CodexForkRewindPointNotFoundError
Codex rewind point ${itemId} not found in thread ${threadId}
Error message
Codex rewind point ${itemId} not found in thread ${threadId} What it means
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.
Source
Thrown at packages/happy-cli/src/codex/codexThreadFork.ts:120
model?: string;
approvalPolicy?: any;
sandbox?: any;
mcpServers?: Record<string, unknown>;
},
): Promise<CodexForkResult> {
const forked = await client.forkThread({
threadId: opts.threadId,
...(opts.cwd ? { cwd: opts.cwd } : {}),
...(opts.model ? { model: opts.model } : {}),
...(opts.approvalPolicy ? { approvalPolicy: opts.approvalPolicy } : {}),
...(opts.sandbox ? { sandbox: opts.sandbox } : {}),
...(opts.mcpServers ? { mcpServers: opts.mcpServers } : {}),
});
if (opts.cutAfterItemId) {
const cutTurn = findCutTurn(forked.thread, opts.cutAfterItemId);
if (!cutTurn) {
throw new CodexForkRewindPointNotFoundError(opts.cutAfterItemId, opts.threadId);
}
const turns = forked.thread.turns ?? [];
const turnsToDrop = turns.length - cutTurn.index;
if (turnsToDrop > 0) {
await client.rollbackThread({
threadId: forked.threadId,
numTurns: turnsToDrop,
});
}
await client.injectItems({
threadId: forked.threadId,
items: [{
type: 'message',
role: 'user',
content: [{ type: 'input_text', text: cutTurn.text }],
}],
});
}View on GitHub (pinned to b824cd0a46)
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
Example fix
// before
await forkCodexThread({ client, threadId, cutAfterItemId: staleId }); // throws
// after
const points = listCodexRewindPoints(thread);
const id = points.find(p => p.itemId === staleId)?.itemId ?? points.at(-1)?.itemId;
await forkCodexThread({ client, threadId, cutAfterItemId: id }); Defensive patterns
Strategy: validation
Validate before calling
import { listCodexRewindPoints } from '@/codex/codexThreadFork';
const validIds = new Set(listCodexRewindPoints(thread).map(p => p.itemId));
if (!validIds.has(cutAfterItemId)) {
throw new Error(`Unknown rewind point ${cutAfterItemId}; pick one of listCodexRewindPoints()`);
} Type guard
function isValidRewindPoint(thread: { turns?: Array<{ items?: Array<{ id?: string }> }> }, itemId: string): boolean {
return (thread.turns ?? []).some(t => (t.items ?? []).some(i => i.id === itemId));
} Try / catch
try {
await forkCodexThread({ client, threadId, cutAfterItemId });
} catch (error) {
if (error instanceof CodexForkRewindPointNotFoundError) {
const points = listCodexRewindPoints(thread);
// respond to RPC caller with refreshed rewind points
} else { throw error; }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- The chosen rewind point is no longer present in the source C
- Codex CLI is not installed Please install Codex CLI using o
- No thread available to resume.
- No active thread. Call startThread first.
- Failed to resume Codex thread ${opts.threadId}: ${reason}
AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31).
Data as JSON: /api/errors/70faf22f2c7bd11b.
Report an issue: GitHub.