openclaw - ✅(Solved) Fix launchd gateway startup is delayed for minutes unless plist sets ProcessType=Interactive [1 pull requests, 2 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#58061Fetched 2026-04-08 01:54:20
View on GitHub
Comments
2
Participants
1
Timeline
3
Reactions
0
Participants
Timeline (top)
commented ×2cross-referenced ×1

On macOS, an OpenClaw gateway launched via LaunchAgent can stay in state = running for minutes without listening on its websocket port. During that window, clients hit handshake timeouts / 1006 abnormal closure. The same gateway starts in a few seconds when run in the foreground, and also starts in a few seconds under launchd if the plist includes ProcessType = Interactive.

This looks like a launchd integration bug in the generated plist rather than a user misconfiguration.

Root Cause

On macOS, an OpenClaw gateway launched via LaunchAgent can stay in state = running for minutes without listening on its websocket port. During that window, clients hit handshake timeouts / 1006 abnormal closure. The same gateway starts in a few seconds when run in the foreground, and also starts in a few seconds under launchd if the plist includes ProcessType = Interactive.

This looks like a launchd integration bug in the generated plist rather than a user misconfiguration.

PR fix notes

PR #58070: daemon: set macOS LaunchAgent ProcessType and WorkingDirectory

Description (problem / solution / changelog)

Summary

  • set generated macOS LaunchAgent plists to ProcessType=Interactive
  • always emit a WorkingDirectory, defaulting to / when no explicit directory is provided
  • cover the new plist defaults in launchd install tests

Testing

  • pnpm exec vitest run src/daemon/launchd.test.ts
  • pnpm lint

Closes #58061 Related to #54861

Changed files

  • src/daemon/launchd-plist.ts (modified, +6/-4)
  • src/daemon/launchd.test.ts (modified, +21/-0)

Code Example

<key>ProcessType</key>
<string>Interactive</string>
<key>WorkingDirectory</key>
<string>/</string>

---

<key>ProcessType</key>
<string>Interactive</string>
<key>WorkingDirectory</key>
<string>/</string>
RAW_BUFFERClick to expand / collapse

Summary

On macOS, an OpenClaw gateway launched via LaunchAgent can stay in state = running for minutes without listening on its websocket port. During that window, clients hit handshake timeouts / 1006 abnormal closure. The same gateway starts in a few seconds when run in the foreground, and also starts in a few seconds under launchd if the plist includes ProcessType = Interactive.

This looks like a launchd integration bug in the generated plist rather than a user misconfiguration.

Environment

  • macOS 26.1 (arm64)
  • Node /opt/homebrew/opt/node@24/bin/node
  • OpenClaw 2026.3.28
  • LaunchAgent label: ai.openclaw.cleancheck
  • Gateway port: 19011

Current generated plist

The installed LaunchAgent contains:

  • RunAtLoad = true
  • KeepAlive = true
  • ThrottleInterval = 1
  • no ProcessType
  • no explicit WorkingDirectory

Reproduction

  1. Install/run the gateway as a LaunchAgent.
  2. Restart it with launchctl kickstart -k gui/$(id -u)/ai.openclaw.cleancheck.
  3. Immediately probe the socket and inspect logs.

Observed timeline before the plist fix:

  • 09:39:09 receives SIGTERM
  • 09:42:01 auth-store work begins
  • 09:42:11 gateway finally starts listening on ws://127.0.0.1:19011

So the process is alive for ~3 minutes before the port is reachable.

Actual behavior

  • launchctl print shows the job as state = running
  • lsof shows no listener on the configured port for a long time
  • clients attempting to connect during that window hit handshake failures / 1006 abnormal closure
  • sampled process time is spent in Node/V8 parsing/compiling JS modules rather than doing network work

Expected behavior

A launchd-started gateway should reach listening on ws://127.0.0.1:<port> in a few seconds, similar to foreground startup.

Key evidence

1) Slow startup only under the default plist

Before the fix, restart timing looked like this:

  • 09:39:09 SIGTERM
  • 09:42:11 listening on ws://127.0.0.1:19011

2) Sample shows the process is stuck in JS parse/compile work

A macOS sample taken during the startup delay shows the main thread deep in Node/V8 parse/compile paths such as:

  • node::contextify::CompileFunctionForCJSLoader
  • v8::ScriptCompiler::CompileFunction
  • v8::internal::Parser::ParseProgram
  • v8::internal::Scanner::ScanString

This suggests the process is being heavily slowed during bundle cold-start rather than blocked on I/O or lock contention.

3) A/B validation with launchd

I created a temporary LaunchAgent using the same command/env but added:

<key>ProcessType</key>
<string>Interactive</string>
<key>WorkingDirectory</key>
<string>/</string>

Result: the gateway started listening in ~4 seconds.

I then applied the same change to the real LaunchAgent and reproduced again:

  • 09:58:31 SIGTERM
  • 09:58:35 auth-store/canvas/heartbeat complete
  • 09:58:35 listening on ws://127.0.0.1:19011

Port probe succeeded at t=4s.

Why this looks like an OpenClaw bug

The user-facing problem is triggered by the LaunchAgent generated/installed by OpenClaw. If the gateway needs better launchd classification to avoid slow cold-start under macOS, that should be encoded in the generated plist or documented as required.

Right now, a default launchd deployment can look "alive" but be unreachable for minutes, which then cascades into websocket handshake errors that are hard to diagnose.

Suggested fix

When generating/installing the macOS LaunchAgent, include at least:

<key>ProcessType</key>
<string>Interactive</string>
<key>WorkingDirectory</key>
<string>/</string>

ProcessType = Interactive appears to be the important change. WorkingDirectory = / just makes the current runtime behavior explicit.

Related issue

This may be related to the broader launchd classification / throttling behavior discussed in #54861.

extent analysis

Fix Plan

To resolve the issue, update the LaunchAgent plist to include the following configuration:

  • Set ProcessType to Interactive
  • Set WorkingDirectory to /

Example plist snippet:

<key>ProcessType</key>
<string>Interactive</string>
<key>WorkingDirectory</key>
<string>/</string>

Steps to apply the fix:

  1. Update the LaunchAgent plist with the above configuration.
  2. Reload the LaunchAgent configuration using launchctl unload and launchctl load.
  3. Restart the gateway using launchctl kickstart -k gui/$(id -u)/ai.openclaw.cleancheck.

Verification

To verify the fix, check the gateway startup time and port availability:

  1. Restart the gateway and monitor the startup time.
  2. Use lsof to check if the gateway is listening on the configured port.
  3. Attempt to connect to the gateway using a client to verify handshake success.

Extra Tips

  • Ensure the ProcessType is set to Interactive to avoid slow cold-start under macOS.
  • Setting WorkingDirectory to / makes the current runtime behavior explicit, but it may not be necessary for all configurations.
  • Refer to the related issue #54861 for more information on launchd classification and throttling behavior.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

A launchd-started gateway should reach listening on ws://127.0.0.1:<port> in a few seconds, similar to foreground startup.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING