#11455: fix(gateway): default gateway.mode to local when unset
cli
commands
stale
Cluster:
Gateway and macOS Improvements
#### Summary
Fixes #10767
On a clean install (after deleting `~/.openclaw`), the generated `openclaw.json` can end up with `gateway.auth` settings but no `gateway.mode`, causing the gateway to block startup with: `"Gateway start blocked: set gateway.mode=local (current: unset)"`.
lobster-biscuit
#### Root Cause
The gateway config builder functions (`applyNonInteractiveGatewayConfig` and `configureGatewayForOnboarding`) rely on `...nextConfig.gateway` spread to preserve `mode` through multiple object updates (auth, port, bind, tailscale). If `mode` gets lost during any of these spreads (e.g., if the caller's initial config didn't have it), the final config ends up with `gateway.auth` but no `gateway.mode`.
#### Fix (three layers)
1. **`src/cli/gateway-cli/run.ts`**: Auto-repair at gateway start — when config exists with gateway settings (auth, port, etc.) but `mode` is missing, default to `"local"` and log a warning. This fixes existing broken configs without requiring manual intervention.
2. **`src/commands/onboard-non-interactive/local/gateway-config.ts`**: Explicitly set `mode: nextConfig.gateway?.mode ?? "local"` in the final gateway object spread instead of relying on preservation through spread.
3. **`src/wizard/onboarding.gateway-config.ts`**: Same defensive explicit set.
#### Behavior Changes
- Gateway now starts successfully even when `gateway.mode` is missing from config (as long as gateway settings exist). Previously it would error and exit.
- Config generation now explicitly preserves `mode` through all spread operations.
- No change to the default value — `"local"` is always the intended default for local gateway mode.
#### Codebase and GitHub Search
- Searched all config-writing paths: `onboarding.ts`, `onboard-non-interactive/local.ts`, `configure.wizard.ts`, `configure.gateway.ts`, `dev.ts`
- Verified that all paths intend to set `mode: "local"` but two relied on spread-preservation
- Confirmed `configure.gateway.ts` (line 210) and `dev.ts` (line 105) already set `mode` explicitly (not affected)
- Confirmed `resolveGatewayPort` pattern exists for port resolution; no equivalent `resolveGatewayMode` existed
#### Tests
- `pnpm build` ✓
- `pnpm check` ✓
- Added 4 unit tests in `gateway-config.test.ts`:
- Preserves existing `mode: "local"` from nextConfig
- Defaults `mode` to `"local"` when nextConfig has no mode (the #10767 bug)
- Defaults `mode` to `"local"` when `nextConfig.gateway` is undefined
- Does not override explicitly set `remote` mode
**Sign-Off**
- AI-assisted: Yes (Cursor + Claude). All changes reviewed and understood.
- Degree of testing: Build + lint + 4 unit tests covering the bug scenario
- Models used: Claude claude-4.6-opus
- Submitter effort: Root cause investigation across 6+ config-writing paths, defensive multi-layer fix
Made with [Cursor](https://cursor.com)
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR addresses a startup blocker where `gateway.auth` (or other gateway settings) can be present in `openclaw.json` while `gateway.mode` is missing, causing the gateway CLI to refuse to start. It fixes this in three places:
- `src/cli/gateway-cli/run.ts`: runtime “auto-repair” by treating missing `gateway.mode` as `local` when a config exists and `gateway` settings are present.
- `src/commands/onboard-non-interactive/local/gateway-config.ts` and `src/wizard/onboarding.gateway-config.ts`: explicitly set `gateway.mode` during config assembly so it can’t be lost across object spreads.
- Adds unit tests for `applyNonInteractiveGatewayConfig`.
The main concern is that the newly added test file appears to have incorrect relative import paths, which would prevent the test suite from compiling/running.
<h3>Confidence Score: 3/5</h3>
- Mostly safe, but test compilation may be broken due to incorrect imports.
- The functional changes are small and targeted (defaulting `gateway.mode` to `local` when unset), but the added unit test file likely has incorrect relative import paths, which would break CI/test runs until fixed.
- src/commands/onboard-non-interactive/local/gateway-config.test.ts
<!-- greptile_other_comments_section -->
<sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#14564: fix(gateway): crashes on startup when tailscale meets non-loopback ...
by yinghaosang · 2026-02-12
84.1%
#19088: fix(gateway): allow startup with unset mode and fix pairing for local…
by mdanassaif · 2026-02-17
83.2%
#8260: fix(macOS): gateway readiness detection + reversible Configure later
by xksteven · 2026-02-03
82.7%
#19937: fix(gateway): validate token/password auth modes and isolate gatewa...
by NewdlDewdl · 2026-02-18
82.3%
#6770: fix(gateway): protect host-local transport fields from config.patch
by ryx2 · 2026-02-02
82.3%
#5823: fix(config): exit cleanly on invalid config instead of high CPU loop
by gavinbmoore · 2026-02-01
81.6%
#4653: fix(gateway): improve crash resilience for mDNS and network errors
by AyedAlmudarra · 2026-01-30
81.2%
#19885: test(gateway,browser): isolate tests from ambient OPENCLAW_GATEWAY_...
by NewdlDewdl · 2026-02-18
81.1%
#21459: fix(gateway): resolve port from profile config, not inherited env
by kkeeling · 2026-02-19
80.9%
#12234: gateway: incident tracking, recover command, and ciao ERR_SERVER_CL...
by levineam · 2026-02-09
80.4%