← Back to PRs

#22671: Preserve custom env vars in LaunchAgent plist across updates

by Clawborn open 2026-02-21 13:53 View on GitHub →
gateway size: S trusted-contributor
## Problem `openclaw update` regenerates the LaunchAgent plist from scratch, wiping user-added `EnvironmentVariables`. This breaks setups that rely on custom env vars like `NODE_OPTIONS` for IPv6 workarounds on Node.js 22+. Users have to manually re-patch the plist after every update. ## Fix Before building the new plist, read the existing one via `readLaunchAgentProgramArgumentsFromFile()` (already available in `launchd-plist.ts`) and merge its environment variables as defaults. Explicitly provided values from the new install take precedence, so managed keys like `OPENCLAW_GATEWAY_TOKEN` are still updated correctly. ```ts const existing = await readLaunchAgentProgramArgumentsFromFile(plistPath); const mergedEnvironment = { ...existing?.environment, ...environment }; ``` ## Test Added test that installs a LaunchAgent with `NODE_OPTIONS`, then reinstalls without it, and verifies `NODE_OPTIONS` is preserved while `OPENCLAW_GATEWAY_TOKEN` is correctly updated to the new value. Closes #22663 <!-- greptile_comment --> <h3>Greptile Summary</h3> Merges existing environment variables from the LaunchAgent plist before reinstall to preserve user-added custom env vars like `NODE_OPTIONS` across updates. Explicitly provided keys still override preserved values, ensuring managed variables like `OPENCLAW_GATEWAY_TOKEN` update correctly. - Reads existing plist via `readLaunchAgentProgramArgumentsFromFile()` (which safely returns null if file doesn't exist) - Merges environment with spread operator: `{...existing?.environment, ...environment}` - Test verifies `NODE_OPTIONS` persists while `OPENCLAW_GATEWAY_TOKEN` updates to new value - Test structure issue: new test is outside the `describe("launchd install")` block <h3>Confidence Score: 4/5</h3> - Safe to merge after fixing test structure issue - Implementation is clean and correct with proper null-safety via optional chaining, but test placement outside describe block breaks test organization conventions - Fix test structure in `src/daemon/launchd.test.ts` (test should be inside describe block) <sub>Last reviewed commit: 817b557</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs