slopus/happy · error
Failed to resume Codex thread ${opts.threadId}: ${reason}
Error message
Failed to resume Codex thread ${opts.threadId}: ${reason} What it means
resumeExistingThread() wraps any failure from client.resumeThread() — network/transport errors, 'Codex CLI is not installed', invalid thread id, etc. — into a single Error prefixed with 'Failed to resume Codex thread <id>: <reason>'. The original message is preserved after the colon.
Source
Thrown at packages/happy-cli/src/codex/resumeExistingThread.ts:56
mcpServers: opts.mcpServers,
});
opts.session.updateMetadata((currentMetadata) => ({
...currentMetadata,
codexThreadId: resumedThread.threadId,
}));
opts.messageBuffer.addMessage(`Resumed thread ${trimIdent(resumedThread.threadId)}`, 'status');
if (opts.announce !== false) {
opts.session.sendSessionEvent({
type: 'message',
message: `Resumed Codex thread ${resumedThread.threadId}`,
});
}
return resumedThread;
} catch (error) {
const reason = error instanceof Error ? error.message : String(error);
throw new Error(`Failed to resume Codex thread ${opts.threadId}: ${reason}`);
}
}
View on GitHub (pinned to b824cd0a46)
Solutions
- Read the `reason` after the colon to identify the root cause and fix that first
- Verify the thread id exists (check Codex session files) and the Codex CLI is installed (`codex --version`)
- If the thread is unrecoverable, start a new thread instead of resuming
- Retry once on transient transport errors before failing
Example fix
// before
throw new Error(`Failed to resume Codex thread ${id}: ${reason}`); // opaque handling upstream
// after
try {
await resumeExistingThread({ ... });
} catch (error) {
if (String(error).includes('not installed')) {
// prompt user to install Codex CLI
} else {
await startNewThread();
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!threadId) {
await startNewThread();
return;
} Try / catch
try {
await resumeExistingThread({ client, session, messageBuffer, threadId, cwd, mcpServers });
} catch (error) {
const msg = (error as Error).message;
if (msg.includes('not installed') || msg.includes('No thread available')) {
await startNewThread();
} else {
throw error; // surface unexpected failures
}
} Prevention
- Always parse the root cause after the 'Failed to resume ... :' prefix
- Validate the thread id exists before attempting resume
- Provide a 'start fresh' fallback path in UX for unrecoverable threads
When it happens
Trigger: Calling resumeExistingThread() where the underlying resumeThread() throws for any reason: missing Codex CLI, nonexistent/deleted thread id, app-server crash, or IO failure in the target cwd.
Common situations: Resuming a Codex session whose rollout file was deleted; `happy codex --resume` with an id from another machine; Codex CLI upgraded/removed between runs; this wrapper appearing in logs when the root cause is error 60/61.
Related errors
- No thread available to resume.
- No active thread. Call startThread first.
- No active Codex thread
- Happy session ${session.id} is missing its Codex thread ID.
- The chosen rewind point is no longer present in the source C
AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31).
Data as JSON: /api/errors/a804e773abea24f3.
Report an issue: GitHub.