Summary
PR #54536 (merged Mar 29, shipped from v2026.4.1 onwards) added a local-direct fallback in authorizeGatewayConnect so that requests arriving on loopback without proxy identity headers authenticate via auth.token instead of being rejected. This is the intended fix for the long-standing issue that internal callers (cron tool, agent backend exec) could not authenticate in trusted-proxy mode (#17761, #45217, #50580).
However, the startup validator in the same file (src/gateway/auth.ts, validateAuthConfig) still throws when both are configured:
if (auth.mode === "trusted-proxy") {
...
if (auth.token) {
throw new Error(
"gateway auth mode is trusted-proxy, but a shared token is also configured; remove gateway.auth.token / OPENCLAW_GATEWAY_TOKEN because trusted-proxy and token auth are mutually exclusive",
);
}
}
The net effect: the local-direct fallback added by #54536 is unreachable. Any config that would exercise it is rejected at boot.
Reproduction
- Deploy OpenClaw v2026.4.10 (or current
main) behind nginx with gateway.auth.mode = "trusted-proxy" and a user-header cookie wall.
- From inside the running agent, ask it to schedule a cron (or use any tool that uses the internal loopback gateway).
- The agent adds
OPENCLAW_GATEWAY_TOKEN / gateway.auth.token to enable cron auth (as documented as the workaround).
- Container restart → gateway boot → validator throws
"... mutually exclusive" → crash loop → 502 at the reverse proxy.
Observed 2026-04-16 on our production-style deployment (trusted-proxy + nginx cookie wall for browser, agent tried to use cron tool → gateway crash loop → users locked out with Bad Gateway). We recovered by stripping the token out of both openclaw.json (gateway.auth.token) and container.env (OPENCLAW_GATEWAY_TOKEN), which forced the user back to square one (no cron tool).
Proposed fix
Remove (or relax) the throw in validateAuthConfig. auth.token is now a valid + required setting when mode === "trusted-proxy" if operators want the cron/exec tools to work; the runtime correctly routes:
The check contradicts the fix. A lighter error message / doc reference in the log would be sufficient, but the hard abort should go.
Environment
- OpenClaw v2026.4.10 (Docker, ARM64, Amazon Linux 2023)
- Reverse proxy: nginx with
X-Forwarded-User header (trusted-proxy mode)
gateway.auth.trustedProxy.userHeader = "X-Forwarded-User"
- Reported via: https://github.com/openclaw/openclaw/blob/main/src/gateway/auth.ts (lines around the validator + the
isLocalDirectRequest block near authorizeGatewayConnect)
Related: #17761, #45217, #50580, #54536
Summary
PR #54536 (merged Mar 29, shipped from v2026.4.1 onwards) added a local-direct fallback in
authorizeGatewayConnectso that requests arriving on loopback without proxy identity headers authenticate viaauth.tokeninstead of being rejected. This is the intended fix for the long-standing issue that internal callers (cron tool, agent backend exec) could not authenticate intrusted-proxymode (#17761, #45217, #50580).However, the startup validator in the same file (
src/gateway/auth.ts,validateAuthConfig) still throws when both are configured:The net effect: the local-direct fallback added by #54536 is unreachable. Any config that would exercise it is rejected at boot.
Reproduction
main) behind nginx withgateway.auth.mode = "trusted-proxy"and a user-header cookie wall.OPENCLAW_GATEWAY_TOKEN/gateway.auth.tokento enable cron auth (as documented as the workaround)."... mutually exclusive"→ crash loop → 502 at the reverse proxy.Observed 2026-04-16 on our production-style deployment (trusted-proxy + nginx cookie wall for browser, agent tried to use cron tool → gateway crash loop → users locked out with Bad Gateway). We recovered by stripping the token out of both
openclaw.json(gateway.auth.token) andcontainer.env(OPENCLAW_GATEWAY_TOKEN), which forced the user back to square one (no cron tool).Proposed fix
Remove (or relax) the throw in
validateAuthConfig.auth.tokenis now a valid + required setting whenmode === "trusted-proxy"if operators want the cron/exec tools to work; the runtime correctly routes:authorizeTrustedProxyauthorizeTokenAuth(introduced in fix(gateway/auth): local trusted-proxy fallback to require token auth #54536)The check contradicts the fix. A lighter error message / doc reference in the log would be sufficient, but the hard abort should go.
Environment
X-Forwarded-Userheader (trusted-proxy mode)gateway.auth.trustedProxy.userHeader = "X-Forwarded-User"isLocalDirectRequestblock nearauthorizeGatewayConnect)Related: #17761, #45217, #50580, #54536