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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions