fix: detect OpenClaw-managed launchd/systemd services in process respawn

restartGatewayProcessWithFreshPid() checks SUPERVISOR_HINT_ENV_VARS to
decide whether to let the supervisor handle the restart (mode=supervised)
or to fork a detached child (mode=spawned). The existing list only had
native launchd vars (LAUNCH_JOB_LABEL, LAUNCH_JOB_NAME) and systemd vars
(INVOCATION_ID, SYSTEMD_EXEC_PID, JOURNAL_STREAM).

macOS launchd does NOT automatically inject LAUNCH_JOB_LABEL into the
child environment. OpenClaw's own plist generator (buildServiceEnvironment
in service-env.ts) sets OPENCLAW_LAUNCHD_LABEL instead. So on stock macOS
LaunchAgent installs, isLikelySupervisedProcess() returned false, causing
the gateway to fork a detached child on SIGUSR1 restart. The original
process then exits, launchd sees its child died, respawns a new instance
which finds the orphan holding the port — infinite crash loop.

Fix: add OPENCLAW_LAUNCHD_LABEL, OPENCLAW_SYSTEMD_UNIT, and
OPENCLAW_SERVICE_MARKER to the supervisor hint list. These are set by
OpenClaw's own service environment builders for both launchd and systemd
and are the reliable supervised-mode signals.

Fixes #27605
This commit is contained in:
taw0002
2026-02-26 10:03:29 -05:00
committed by Peter Steinberger
parent 5c0255477c
commit 792ce7b5b4
2 changed files with 37 additions and 0 deletions

View File

@@ -9,11 +9,21 @@ export type GatewayRespawnResult = {
};
const SUPERVISOR_HINT_ENV_VARS = [
// macOS launchd — native env vars (may be set by launchd itself)
"LAUNCH_JOB_LABEL",
"LAUNCH_JOB_NAME",
// macOS launchd — OpenClaw's own plist generator sets these via
// buildServiceEnvironment() in service-env.ts. launchd does NOT
// automatically inject LAUNCH_JOB_LABEL into the child environment,
// so OPENCLAW_LAUNCHD_LABEL is the reliable supervised-mode signal.
"OPENCLAW_LAUNCHD_LABEL",
// Linux systemd
"INVOCATION_ID",
"SYSTEMD_EXEC_PID",
"JOURNAL_STREAM",
"OPENCLAW_SYSTEMD_UNIT",
// Generic service marker (set by both launchd and systemd plist/unit generators)
"OPENCLAW_SERVICE_MARKER",
];
function isTruthy(value: string | undefined): boolean {