Skip to content
This repository was archived by the owner on Feb 14, 2026. It is now read-only.
This repository was archived by the owner on Feb 14, 2026. It is now read-only.

Remote session spawn timeout due to macOS background process throttling #166

@TeigenZhang

Description

@TeigenZhang

Problem

When starting a new session remotely (from the Happy Coder mobile app) while the Mac has no foreground activity (lid closed / screen locked), the spawned happy claude child processes get throttled by macOS and fail to send the session webhook within the 15-second timeout.

Reproduction Steps

  1. Start the daemon on macOS (happy daemon start-sync)
  2. Close the laptop lid or lock the screen (keep the machine awake via caffeinate/daemon)
  3. From the Happy Coder mobile app, try to start a new session on that machine
  4. The spawn request times out with [RPC] [ERROR] Error handling request {"error":{}}
  5. Retry multiple times — each retry spawns another zombie process

Observed Behavior

From the daemon log:

[11:51:01] [SPAWN HAPPY CLI] Spawning: happy claude --happy-starting-mode remote --started-by daemon
[11:51:01] [DAEMON RUN] Spawned process with PID 90269
[11:51:01] [DAEMON RUN] Waiting for session webhook for PID 90269
[11:51:16] [DAEMON RUN] Session webhook timeout for PID 90269
[11:51:16] [RPC] [ERROR] Error handling request {"error":{}}

This happened 5 times between 11:51-11:55. All 5 processes woke up simultaneously at 12:28:21 (37 minutes later), confirming macOS was throttling them:

# All 5 child process logs start at the exact same second:
[12:28:21.575] Starting happy CLI with args: ... claude --happy-starting-mode remote --started-by daemon
[12:28:21.577] Starting happy CLI with args: ... claude --happy-starting-mode remote --started-by daemon
[12:28:21.575] Starting happy CLI with args: ... claude --happy-starting-mode remote --started-by daemon
...

Later spawns at 12:59 (when the machine was actively used) succeeded within 3 seconds.

Root Cause

macOS aggressively throttles background processes that:

  • Have no TTY (shown as ?? in ps output)
  • Have no direct user interaction context
  • Are spawned by a background daemon

The daemon itself stays alive (it has caffeinate and was spawned from a terminal), but its child processes inherit a lower scheduling priority.

Impact

  • Zombie happy claude processes accumulate (each retry spawns a new one)
  • These zombies consume memory (~34MB each) and potentially API connections
  • The mobile app shows an error, and the user has no way to know the sessions will eventually start
  • Manual cleanup (kill zombie PIDs) is required

Suggested Fixes

  1. Wrap child process spawn with caffeinate -i to prevent macOS from throttling the child:

    // Instead of spawning `happy claude ...` directly:
    spawn('caffeinate', ['-i', 'happy', 'claude', '--happy-starting-mode', 'remote', ...])
  2. Increase the webhook timeout (e.g., 60s or 120s) for remote-spawned sessions, since macOS scheduling delays can far exceed 15s

  3. Deduplicate spawn requests — if a spawn for the same directory is already pending/in-progress, don't spawn another process

  4. Kill timed-out child processes — if the webhook times out, send SIGTERM to the spawned PID to prevent zombie accumulation

Environment

  • happy-coder: 0.13.0
  • Claude Code: 2.1.39
  • Node.js: v24.4.1
  • macOS: Darwin 24.6.0 (arm64, Apple Silicon)
  • Machine: MacBook Pro

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