Skip to content

Session permanently stuck after abort — CLI resumes dead Claude Code session instead of starting fresh #145

@sartman-twilio

Description

@sartman-twilio

Summary

After pressing Escape (or any other abort action) during an active session, the session becomes permanently stuck. Every subsequent message triggers Claude Code to start, initialize fully (MCP connects, SessionStart hook runs), then immediately exit with code 1. The only workaround is to manually kill the session process, losing all conversation context.

Steps to Reproduce

  1. Start a session via the web UI
  2. Wait for Claude to respond with an AskUserQuestion (e.g., permission prompt, radio buttons)
  3. Press Escape to dismiss/abort
  4. Send a new message to the same session
  5. Expected: Fresh Claude Code session starts and processes the message
  6. Actual: Claude Code starts, runs SessionStart hook successfully, then immediately exits with code 1. Repeats on every subsequent message.

Root Cause

In claudeRemoteLauncher.ts, after an abort causes Claude Code to exit with code 1, the error handler sets waitForMessageBeforeNextLaunch = true and continues the loop — but never clears the stored Claude Code session ID.

On the next loop iteration:

  1. session.sessionId still holds the dead Claude Code session ID (e.g., 1a59715d-...)
  2. resolveClaudeRemoteSessionStartPlan() uses it as startFrom
  3. The Agent SDK passes --resume <dead-session-id> to Claude Code
  4. Claude Code cannot resume an aborted session → exits with code 1
  5. The cycle repeats indefinitely

Relevant code path (claudeRemoteLauncher.ts):

// Line ~952 — exitCode === 1 handler
} else {
    const exitCode = resolveClaudeCodeExitCode(e);
    if (exitCode === 1) {
        // ... formats error message ...
        session.client.sendSessionEvent({ type: 'message', message });
        waitForMessageBeforeNextLaunch = true;  // ← waits for next message
        continue;  // ← but session.sessionId still points to dead session!
    }
}

And:

// Line ~947 — abortError handler
} else if (abortError) {
    if (controller.signal.aborted) {
        session.client.sendSessionEvent({ type: 'message', message: 'Aborted by user' });
    }
    continue;  // ← same problem: session ID not cleared
}

Meanwhile, the onSessionReset callback already has the correct pattern:

onSessionReset: () => {
    forceNewSession = true;
    session.clearSessionId();  // ← this is what the error handlers need
},

Proposed Fix

Add forceNewSession = true and session.clearSessionId() to both error handlers so the next launch creates a fresh Claude Code session:

// In the abortError handler:
} else if (abortError) {
    if (controller.signal.aborted) {
        session.client.sendSessionEvent({ type: 'message', message: 'Aborted by user' });
    }
    forceNewSession = true;       // ← ADD
    session.clearSessionId();     // ← ADD
    continue;
}

// In the exitCode === 1 handler:
if (exitCode === 1) {
    // ... existing error formatting ...
    session.client.sendSessionEvent({ type: 'message', message });
    if (controller.signal.aborted) {          // ← ADD
        forceNewSession = true;               // ← ADD
        session.clearSessionId();             // ← ADD
    }
    waitForMessageBeforeNextLaunch = true;
    continue;
}

Environment

  • Happier CLI: 0.1.0-dev.1775063171.91734 (dev branch)
  • Claude Code: v2.1.69
  • Agent SDK: 0.2.56
  • Platform: Linux (Docker container)

Debug Evidence

From the session process log after abort + relaunch:

[22:29:36.925] [remote]: launch error {"message":"Claude Code process exited with code 1"...}
[22:29:36.927] [remote]: launch finally
[22:29:36.927] [remote]: launch done
[22:29:36.976] [remote]: launch
[22:29:36.976] [remote]: Continuing existing session: 1a59715d-35d7-433d-b1ba-a890c6c65279  ← dead session

From the Claude Code debug output on the failed resume:

SessionStart hook succeeds: "Vault is unlocked. Credentials expire in 479 minutes."
LSP server manager shut down successfully           ← 1ms later, immediate shutdown
MCP server "happier": UNKNOWN connection closed after 0s (cleanly)
SessionEnd fires

cc @leeroybrun

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions