Skip to content

[desktop] Crash screen shown while the supervised daemon is alive: launchd ProcessType=Background starves it and the 1.5 s health probe gives up #2179

Description

@harrisrobin

Summary

The desktop app shows the crash screen while the supervised daemon is alive and well. The daemon's /api/health intermittently takes 0.5–15 s to answer while idle (0% CPU, nothing in its log), and the desktop monitor treats three missed 1.5 s probes as a dead daemon. Two things make this trip on a loaded Mac: the launchd unit runs the daemon as ProcessType=Background (scheduler priority 4), and the probe budget is 1.5 s × 3 attempts × 3 consecutive misses.

Environment

  • Executor desktop 1.6.10, packaged, macOS 27.2 (Darwin 27.2.0), arm64
  • Daemon: bundled executor daemon run --foreground under sh.executor.daemon (Bun v1.3.11 binary)
  • Machine under heavy load at the time: load average 39 / 83 / 115 on 10 cores, 26 GB in the memory compressor

What the logs show

~/Library/Logs/Executor/main.log over 2026-10-01 → 10-02:

  • showCrashScreen fired five times on 10-01 (08:14, 19:46, 21:30, 21:33, 21:36) while the manifest pid stayed 1141 the whole two days.
  • Hundreds of supervised daemon at http://localhost:4789 (pid 1141) did not answer the health probe; keeping its manifest because the process is still alive, in clusters lasting 1–5 minutes.
  • No Crashpad dumps, no macOS DiagnosticReports for the daemon.

Live measurement against the idle daemon

$ for i in $(seq 6); do curl -s -o /dev/null -w 'connect=%{time_connect} ttfb=%{time_starttransfer}\n' http://127.0.0.1:4789/api/health; done
connect=0.000826 ttfb=7.343603
connect=0.000318 ttfb=0.812192
connect=0.000374 ttfb=0.547723
connect=0.000606 ttfb=0.084973
connect=0.001535 ttfb=5.085419
connect=0.000723 ttfb=1.041224

During those seconds: top reports the daemon at 0.0% CPU, state sleeping; daemon.log gets no new lines; sample shows the main thread parked in _pthread_cond_wait / syscall_thread_switch inside the executor binary (no libsql.node or keyring.node frames on the main thread). A 75-probe run at 250 ms spacing: 4 probes over 0.5 s, all in one 7-second window with no daemon activity logged. A later 60-probe run at 1 s spacing: 13 over 0.5 s, max 9.1 s, one earlier probe took 15.3 s.

ps -o pri for the daemon reports 4 (launchd Background class); the Executor GUI process reports 46, a user-launched bun server 37. taskpolicy -B -p <pid> does not change it for a launchd-managed job.

Why this looks like a crash to the user

apps/desktop/src/main/index.ts: SUPERVISED_MONITOR_MISSES_BEFORE_DOWN = 3, polled every 10 s, each poll isDaemonReachable = three fetch('/api/health') attempts with a 1.5 s abort. A daemon that stalls for 30 s is declared down and the crash screen replaces the UI. The user then restarts, which triggers the full tool sync again (1–5 min with a dozen remote MCP connections), and the cycle repeats.

Asks

  1. Drop ProcessType=Background from the generated plist in apps/cli/src/service.ts (or make it configurable). The daemon serves interactive UI and MCP calls; background class starves it on a busy machine.
  2. Give the supervised monitor a more forgiving budget (e.g. 5 s probe timeout, or require the manifest pid to be gone before showing the crash screen), and say "daemon unresponsive" rather than "crashed" when the pid is alive.
  3. Separately: the idle-time main-thread stall itself. Happy to run anything that would help pin it (the shipped binary has no symbols).

Diagnostics export (Export Diagnostics…) available on request; it contains no secrets but I would rather attach it to a maintainer than post it publicly.

Confirmation

Removing ProcessType=Background from the installed plist and re-bootstrapping the unit moved the daemon from priority 4 to 20. With the machine even busier (load average 557–589), 60 probes at 1 s spacing: median 14 ms, max 0.56 s, one over 0.5 s. Under the Background class at lower load: 13 of 35 probes over 0.5 s, max 15.3 s.

No activity

Activity on this issue will appear here.

Activity

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

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