slopus/happy · error
No active Codex thread
Error message
No active Codex thread
What it means
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.
Source
Thrown at packages/happy-cli/src/codex/runCodex.ts:631
threadId,
goal: result.goal,
});
messageBuffer.addMessage('Goal updated', 'status');
return true;
} catch (error) {
logger.debug('[Codex] Goal command API failed; falling back to normal turn:', error);
return false;
}
};
session.rpcHandlerManager.registerHandler('goal-action', async (params: Record<string, unknown>) => {
const command = parseCodexGoalActionParams(params);
if (!command) {
throw new Error('Unsupported Codex goal action');
}
const threadId = client.threadId;
if (!threadId) {
throw new Error('No active Codex thread');
}
const handled = await handleCodexGoalCommand(command, threadId);
if (!handled) {
throw new Error('Codex goal actions are not supported by this runtime');
}
return { ok: true };
});
// Approval handler: routes server → client approval requests to our permission handler
client.setApprovalHandler(async (params) => {
const toolName = params.type === 'exec'
? 'CodexBash'
: params.type === 'patch'
? 'CodexPatch'
: (params.toolName ?? 'McpTool');
const input = params.type === 'exec'View on GitHub (pinned to b824cd0a46)
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
Example fix
// before
await rpc('goal-action', { action: 'pause' }); // throws when no thread
// after
if (client.threadId) {
await rpc('goal-action', { action: 'pause' });
} Defensive patterns
Strategy: validation
Validate before calling
if (!client.threadId) {
throw new RpcError('SESSION_NOT_READY', 'Codex thread not started yet; retry after initialization');
}
await rpc('goal-action', params); Type guard
function canSendGoalAction(client: { threadId?: string | null }): boolean {
return typeof client.threadId === 'string' && client.threadId.length > 0;
} Try / catch
try {
await handleGoalAction(params);
} catch (error) {
if ((error as Error).message === 'No active Codex thread') {
// reply to RPC with a retryable 'thread not ready' error
} else { throw error; }
} Prevention
- 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
When it happens
Trigger: A goal-action RPC arrives before startThread/resumeThread completed, or after the thread was closed/lost during reconnection.
Common situations: 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.
Related errors
- No thread available to resume.
- No active thread. Call startThread first.
- The chosen rewind point is no longer present in the source C
- Claude goal action already in progress
- No active Claude goal
AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31).
Data as JSON: /api/errors/aa211807043e7f07.
Report an issue: GitHub.