Bug
When switching from local to remote mode (triggered from mobile), the CLI exits instead of switching. The doSwitch callback correctly sets exitReason = { type: "switch" }, but the subsequent ExitCodeError catch block unconditionally overwrites it with { type: "exit", code: 143 }.
Steps to reproduce
- Start
happy CLI in local mode
- From mobile app, trigger switch to remote mode (press space)
- Expected: CLI switches to remote mode
- Actual: CLI exits completely
Root cause
In the main session loop, when a switch is triggered:
doSwitch sets exitReason = { type: "switch" }
- Claude process receives SIGTERM → exits with code 143
- Catch block handles
ExitCodeError(143)
- Bug: catch block unconditionally sets
exitReason = { type: "exit", code: 143 }, destroying the switch intent
- Loop sees
type === "exit" and terminates
Fix
Guard the ExitCodeError catch to preserve an existing switch intent:
if (e instanceof ExitCodeError) {
if (exitReason && exitReason.type === "switch") {
break; // preserve switch intent — exit code 143 is expected from SIGTERM
}
session.client.closeClaudeSessionTurn("failed");
exitReason = { type: "exit", code: e.exitCode };
break;
}
Confirmed with logging
Added debug logging to the catch block. On reproduction, logs show:
[local]: exitReason at catch time: {"type":"switch"}
[local]: ExitCodeError (code 143) but exitReason is already 'switch', preserving switch intent
With the guard in place, the switch completes successfully.
Environment
- happy 1.1.3 (npm global)
- Node.js v20.19.5
- Ubuntu 22.04, x86_64
Bug
When switching from local to remote mode (triggered from mobile), the CLI exits instead of switching. The
doSwitchcallback correctly setsexitReason = { type: "switch" }, but the subsequentExitCodeErrorcatch block unconditionally overwrites it with{ type: "exit", code: 143 }.Steps to reproduce
happyCLI in local modeRoot cause
In the main session loop, when a switch is triggered:
doSwitchsetsexitReason = { type: "switch" }ExitCodeError(143)exitReason = { type: "exit", code: 143 }, destroying the switch intenttype === "exit"and terminatesFix
Guard the
ExitCodeErrorcatch to preserve an existing switch intent:Confirmed with logging
Added debug logging to the catch block. On reproduction, logs show:
[local]: exitReason at catch time: {"type":"switch"}
[local]: ExitCodeError (code 143) but exitReason is already 'switch', preserving switch intent
With the guard in place, the switch completes successfully.
Environment