Skip to content

fix: preserve switch intent when ExitCodeError is caught in local launcher#974

Open
easyfan wants to merge 1 commit intoslopus:mainfrom
easyfan:fix/local-launcher-preserve-switch-intent
Open

fix: preserve switch intent when ExitCodeError is caught in local launcher#974
easyfan wants to merge 1 commit intoslopus:mainfrom
easyfan:fix/local-launcher-preserve-switch-intent

Conversation

@easyfan
Copy link
Copy Markdown

@easyfan easyfan commented Apr 3, 2026

Problem

Fixes #941.

When the user sends a message from the mobile app while in local (PTY) mode, doSwitch() is called which:

  1. Sets exitReason = { type: 'switch' }
  2. Sends SIGTERM to the claude PTY process via abort()

The PTY process exits with code 143 (SIGTERM), throwing an ExitCodeError. The catch block was unconditionally overwriting exitReason with { type: 'exit', code: 143 }, so instead of transitioning to remote mode, the entire process terminated.

Fix

Guard the ExitCodeError handler: if exitReason is already set (meaning doSwitch or doAbort already handled the intent), break without overwriting. The SIGTERM exit code in this case is expected — it's the result of us sending the signal ourselves.

 if (e instanceof ExitCodeError) {
+    if (exitReason) {
+        break; // preserve existing exit reason (e.g. switch intent) — SIGTERM is expected
+    }
     session.client.closeClaudeSessionTurn('failed');
     exitReason = { type: 'exit', code: e.exitCode };
     break;
 }

Testing

Reproduced by running happy locally, sending a message from the iOS app, and observing the process terminate instead of switching to remote mode. With this fix applied, the transition to remote mode completes correctly.

Diagnosed and fixed with Claude Code via Happy.

…ncher

When switching from local to remote mode (triggered from mobile), calling
doSwitch() sets exitReason = { type: 'switch' } and sends SIGTERM to the
claude PTY process. The process then exits with code 143, throwing an
ExitCodeError — which the catch block was unconditionally overwriting
exitReason with { type: 'exit', code: 143 }, causing the whole process
to terminate instead of transitioning to remote mode.

Fix: guard the ExitCodeError handler to only set exitReason when it hasn't
already been set. If exitReason is already set (e.g. due to doSwitch),
we break without overwriting — the SIGTERM exit code is expected and
should not be treated as an unexpected failure.

Fixes slopus#941

Diagnosed and fixed with Claude Code via Happy.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@bra1nDump
Copy link
Copy Markdown
Contributor

Nice catch, merging — thanks @easyfan!

if you ever have 15 min to show us how you use Happy (or what's annoying), would really help us figure out what to build next: https://calendar.app.google/pozvqGvveyKpVUFh9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI exits instead of switching to remote mode (ExitCodeError overrides switch intent)

2 participants