{"record":{"id":"a804e773abea24f3","repo":"slopus/happy","slug":"failed-to-resume-codex-thread-opts-threadid","errorCode":null,"errorMessage":"Failed to resume Codex thread ${opts.threadId}: ${reason}","messagePattern":"Failed to resume Codex thread (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/happy-cli/src/codex/resumeExistingThread.ts","lineNumber":56,"sourceCode":"            mcpServers: opts.mcpServers,\n        });\n\n        opts.session.updateMetadata((currentMetadata) => ({\n            ...currentMetadata,\n            codexThreadId: resumedThread.threadId,\n        }));\n        opts.messageBuffer.addMessage(`Resumed thread ${trimIdent(resumedThread.threadId)}`, 'status');\n        if (opts.announce !== false) {\n            opts.session.sendSessionEvent({\n                type: 'message',\n                message: `Resumed Codex thread ${resumedThread.threadId}`,\n            });\n        }\n\n        return resumedThread;\n    } catch (error) {\n        const reason = error instanceof Error ? error.message : String(error);\n        throw new Error(`Failed to resume Codex thread ${opts.threadId}: ${reason}`);\n    }\n}\n","sourceCodeStart":38,"sourceCodeEnd":59,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/codex/resumeExistingThread.ts#L38-L59","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nthrow new Error(`Failed to resume Codex thread ${id}: ${reason}`); // opaque handling upstream\n\n// after\ntry {\n  await resumeExistingThread({ ... });\n} catch (error) {\n  if (String(error).includes('not installed')) {\n    // prompt user to install Codex CLI\n  } else {\n    await startNewThread();\n  }\n}","handlingStrategy":"try-catch","validationCode":"if (!threadId) {\n  await startNewThread();\n  return;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await resumeExistingThread({ client, session, messageBuffer, threadId, cwd, mcpServers });\n} catch (error) {\n  const msg = (error as Error).message;\n  if (msg.includes('not installed') || msg.includes('No thread available')) {\n    await startNewThread();\n  } else {\n    throw error; // surface unexpected failures\n  }\n}","preventionTips":["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"],"tags":["codex","thread","resume","error-wrapping"],"backgroundTag":"thread-resume-failed","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}