{"record":{"id":"aa211807043e7f07","repo":"slopus/happy","slug":"no-active-codex-thread","errorCode":null,"errorMessage":"No active Codex thread","messagePattern":"No active Codex thread","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/happy-cli/src/codex/runCodex.ts","lineNumber":631,"sourceCode":"                threadId,\n                goal: result.goal,\n            });\n            messageBuffer.addMessage('Goal updated', 'status');\n            return true;\n        } catch (error) {\n            logger.debug('[Codex] Goal command API failed; falling back to normal turn:', error);\n            return false;\n        }\n    };\n    session.rpcHandlerManager.registerHandler('goal-action', async (params: Record<string, unknown>) => {\n        const command = parseCodexGoalActionParams(params);\n        if (!command) {\n            throw new Error('Unsupported Codex goal action');\n        }\n\n        const threadId = client.threadId;\n        if (!threadId) {\n            throw new Error('No active Codex thread');\n        }\n\n        const handled = await handleCodexGoalCommand(command, threadId);\n        if (!handled) {\n            throw new Error('Codex goal actions are not supported by this runtime');\n        }\n\n        return { ok: true };\n    });\n\n    // Approval handler: routes server → client approval requests to our permission handler\n    client.setApprovalHandler(async (params) => {\n        const toolName = params.type === 'exec'\n            ? 'CodexBash'\n            : params.type === 'patch'\n                ? 'CodexPatch'\n                : (params.toolName ?? 'McpTool');\n        const input = params.type === 'exec'","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/codex/runCodex.ts#L613-L649","documentation":"Inside the 'goal-action' RPC handler, after parsing succeeds, the code reads client.threadId to know which thread the action targets. If it is null/undefined the session has no active Codex thread, so the action cannot be applied and it throws.","triggerScenarios":"A goal-action RPC arrives before startThread/resumeThread completed, or after the thread was closed/lost during reconnection.","commonSituations":"User taps a goal control in the mobile app while the Codex session is still initializing or already dead; reconnection left client._threadId unset; goal action sent to a session that was started with Claude, not Codex.","solutions":["Ensure the Codex thread is started/resumed before issuing goal actions","Reconnect and resume the thread, then retry the action","Guard the client UI so goal controls are disabled until threadId exists","Catch this in the RPC layer and return a 'no active thread' status to the app"],"exampleFix":"// before\nawait rpc('goal-action', { action: 'pause' }); // throws when no thread\n\n// after\nif (client.threadId) {\n  await rpc('goal-action', { action: 'pause' });\n}","handlingStrategy":"validation","validationCode":"if (!client.threadId) {\n  throw new RpcError('SESSION_NOT_READY', 'Codex thread not started yet; retry after initialization');\n}\nawait rpc('goal-action', params);","typeGuard":"function canSendGoalAction(client: { threadId?: string | null }): boolean {\n  return typeof client.threadId === 'string' && client.threadId.length > 0;\n}","tryCatchPattern":"try {\n  await handleGoalAction(params);\n} catch (error) {\n  if ((error as Error).message === 'No active Codex thread') {\n    // reply to RPC with a retryable 'thread not ready' error\n  } else { throw error; }\n}","preventionTips":["Gate goal-action RPC registration until the thread is started","Disable goal controls in the app until the session reports threadId","Re-resume the thread on reconnect before accepting RPCs"],"tags":["codex","thread","rpc","state"],"backgroundTag":"no-active-thread","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}