slopus/happy · error · Error
ACP session is not started
Error message
ACP session is not started
What it means
runAcp's message loop batches user prompts and only forwards them once the ACP session handshake produced an acpSessionId. If a prompt arrives and acpSessionId is still unset (session start never completed or failed), the loop throws this error before sending the turn.
Source
Thrown at packages/happy-cli/src/agent/acp/runAcp.ts:910
logAcp('muted', `Outgoing models from ${opts.agentName}: not reported yet`);
}
}
while (!shouldExit) {
const waitSignal = abortController.signal;
const batch = await messageQueue.waitForMessagesAndGetAsString(waitSignal);
if (!batch) {
if (shouldExit) {
break;
}
if (waitSignal.aborted) {
continue;
}
break;
}
if (!acpSessionId) {
throw new Error('ACP session is not started');
}
logAcp('incoming', `Incoming prompt: ${formatUnknownForConsole(batch.message, ACP_EVENT_PREVIEW_CHARS)}`);
sendEnvelopes(sessionManager.startTurn());
const turnEnded = waitForTurnEnd();
try {
if (typeof batch.mode.permissionMode === 'string' && batch.mode.permissionMode.length > 0) {
await switchPermissionModeIfRequested(batch.mode.permissionMode);
}
if (typeof batch.mode.model === 'string' && batch.mode.model.length > 0) {
await switchModelIfRequested(batch.mode.model);
}
await backend.sendPrompt(acpSessionId, batch.message);
await turnEnded;
sendEnvelopes(sessionManager.endTurn('completed'));
session.sendSessionEvent({ type: 'ready' });
if (verbose) {
logAcp('muted', `Outgoing prompt completion from ${opts.agentName}`);View on GitHub (pinned to b824cd0a46)
Solutions
- Wait for the 'session started'/ready indicator before sending the first prompt.
- Check earlier logAcp/status output for a session-start failure and restart the backend.
- If the agent hangs at startup, verify the command runs and completes the ACP handshake standalone.
- Increase startup timeouts if the agent is slow but healthy.
Example fix
// before
await runAcp({ agentName: 'claude-code' }); // prompt sent instantly
// after
await ready; // wait for session-ready promise
await runAcp({ agentName: 'claude-code' }); Defensive patterns
Strategy: validation
Validate before calling
await sessionReadyPromise; // resolves when acpSessionId is set // only then allow prompts: submitPromptButton.enabled = true;
Type guard
function sessionStarted(state: { acpSessionId?: string | null }): state is { acpSessionId: string } {
return typeof state.acpSessionId === 'string' && state.acpSessionId.length > 0;
} Try / catch
try {
await waitForTurnEnd();
} catch (err) {
if (err instanceof Error && err.message === 'ACP session is not started') {
console.error('Agent session never started; check agent startup logs and restart.');
} else throw err;
} Prevention
- Disable prompt input until the session-ready event fires.
- Investigate session-start failures instead of letting prompts queue behind them.
- Test slow-starting agents to confirm the UI waits for the handshake.
- Add a startup timeout with a clear error if the agent never completes the handshake.
When it happens
Trigger: User submits a prompt while the agent's initialize/newSession handshake is still in flight; session start failed earlier (agent error/crash) but the UI still accepted input; first prompt raced ahead of session establishment.
Common situations: Typing immediately after launching `happy acp <agent>` on a slow-to-start agent; agent binary that hangs during handshake; retrying a prompt after a failed session without restarting.
Related errors
- Session not started
- ACP session not started
- Gemini backend or session not initialized
- Backend has been disposed
- Failed to create stdio pipes
AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31).
Data as JSON: /api/errors/ff3d255d386df18b.
Report an issue: GitHub.