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

  1. Refresh rewind points with listCodexRewindPoints(thread) and use a current item id
  2. Verify cutAfterItemId comes from the same threadId being forked
  3. Omit cutAfterItemId to fork the full thread when rewind isn't essential
  4. 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

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


AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31). Data as JSON: /api/errors/70faf22f2c7bd11b. Report an issue: GitHub.