{"record":{"id":"e531f985ac3d079f","repo":"slopus/happy","slug":"no-thread-available-to-resume","errorCode":null,"errorMessage":"No thread available to resume.","messagePattern":"No thread available to resume\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/happy-cli/src/codex/codexAppServerClient.ts","lineNumber":831,"sourceCode":"        this._threadId = result.thread.id;\n        this._turnId = null;\n        this.rawSubagentActivitySignaturesByItemId.clear();\n        this.rememberThreadDefaults(opts);\n        logger.debug('[CodexAppServer] Thread started:', this._threadId);\n        return { threadId: result.thread.id, model: result.model };\n    }\n\n    async resumeThread(opts?: {\n        threadId?: string;\n        model?: string;\n        cwd?: string;\n        approvalPolicy?: ApprovalPolicy;\n        sandbox?: SandboxMode;\n        mcpServers?: Record<string, unknown>;\n    }): Promise<{ threadId: string; model: string }> {\n        const threadId = opts?.threadId ?? this._threadId;\n        if (!threadId) {\n            throw new Error('No thread available to resume.');\n        }\n\n        const defaults = this.threadDefaults ?? {};\n        const params: ResumeConversationParams = {\n            threadId,\n            model: opts?.model ?? defaults.model ?? null,\n            modelProvider: null,\n            cwd: opts?.cwd ?? defaults.cwd ?? process.cwd(),\n            approvalPolicy: opts?.approvalPolicy ?? defaults.approvalPolicy ?? null,\n            sandbox: opts?.sandbox ?? defaults.sandbox ?? null,\n            config: this.buildThreadConfig(opts?.mcpServers ?? defaults.mcpServers),\n            baseInstructions: null,\n            developerInstructions: null,\n            persistExtendedHistory: true,\n        };\n\n        const result = await this.request('thread/resume', params) as ResumeConversationResponse;\n        this._threadId = result.thread.id;","sourceCodeStart":813,"sourceCodeEnd":849,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/codex/codexAppServerClient.ts#L813-L849","documentation":"The client's resumeThread() requires a thread id, taken from opts.threadId or the client's internal _threadId. If neither is set there is no existing Codex conversation to resume, so it throws. Starting a new thread (startThread) is required before resuming.","triggerScenarios":"Calling resumeThread() on a freshly constructed CodexAppServerClient with no opts.threadId and before startThread() has ever run, or after the client was reset so _threadId is null.","commonSituations":"Trying to `happy codex --resume <id>` with a wrong/empty thread id; resuming a side chat whose thread id metadata was never persisted; reconnecting a session whose codexThreadId metadata is missing.","solutions":["Pass the thread id explicitly: client.resumeThread({ threadId: '<id>', ... })","Call startThread() first if you intend a new conversation, not a resume","Check session metadata (codexThreadId) actually contains a stored thread id before resuming","Fall back to starting a new thread when no id is available"],"exampleFix":"// before\nawait client.resumeThread({ cwd, mcpServers }); // throws: no thread\n\n// after\nconst threadId = session.metadata.codexThreadId;\nif (threadId) {\n  await client.resumeThread({ threadId, cwd, mcpServers });\n} else {\n  await client.startThread({ cwd, mcpServers });\n}","handlingStrategy":"validation","validationCode":"const threadId = opts.threadId ?? client.threadId;\nif (!threadId) {\n  // fall back to a new thread instead of resuming\n  await client.startThread({ cwd, mcpServers });\n} else {\n  await client.resumeThread({ threadId, cwd, mcpServers });\n}","typeGuard":"function hasThreadId(client: { threadId?: string | null }): client is { threadId: string } {\n  return typeof client.threadId === 'string' && client.threadId.length > 0;\n}","tryCatchPattern":"try {\n  await client.resumeThread({ threadId, cwd, mcpServers });\n} catch (error) {\n  if ((error as Error).message === 'No thread available to resume.') {\n    await client.startThread({ cwd, mcpServers });\n  } else { throw error; }\n}","preventionTips":["Persist codexThreadId in session metadata immediately after thread creation","Only call resumeThread when a stored thread id exists","Default to startThread for brand-new sessions"],"tags":["codex","thread","state","missing-argument"],"backgroundTag":"no-active-thread","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}