You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Deterministic repro: any channel-tool invocation on the warm daemon path (POST /tools/invoke or a /v1/chat/completions turn) fails tool execution failed; the in-sandbox gateway log shows pairing required: device is not approved yet on a 10.200.0.1 → 10.200.0.2:18789 connection.
Packet capture of both proxy legs inside the sandbox network namespaces shows the dial-back's connect frame carries auth.token byte-identical (SHA-256-compared) to gateway.auth.token — the credential is not dropped by the reviewed dist patch or the OpenShell relay; the gateway still answers NOT_PAIRED and closes 1008.
Cause confirmed in OpenClaw v2026.6.10 source: OpenShell's transparent proxy re-originates the connection from the sandbox veth peer, so isSharedSecretLoopbackLocalEquivalent (src/gateway/server/ws-connection/handshake-auth-helpers.ts:144) fails only its isLoopbackAddress(remoteAddress) conjunct → pairing locality degrades to remote → shouldSkipLocalBackendSelfPairing (:257) stops exempting the backend gateway-client → requirePairing("not-paired") (message-handler.ts:1561).
The pairing demand can never be satisfied: in-sandbox device-identity persistence fails, so every run mints a fresh device identity, and the auto-pair watcher only approves cli/openclaw-cli/openclaw-control-ui. This appliance accumulated 10,855 paired entries; the watcher's own CLI polling mints and self-approves a new cli identity every ~13 s.
Two independent fixes verified live on the appliance, confirming the mechanism from both sides: (a) an OPENSHELL_SANDBOX=1-gated dist patch treating private-range peers as loopback-equivalent in that one locality check (~7.7 s warm turn); (b) the fix(whatsapp): pair login over the loopback gateway and preserve npm plugin provenance #6645-pattern env-boundary fix — launching the gateway daemon with env -u OPENCLAW_GATEWAY_URL (extending nemoclaw-start.sh's existing CLI scrub idiom to its three gateway launch/respawn sites) so the embedded bridge resolves config loopback — which passes the same checks on a pristine dist (~6.5 s) and mints no pairing records at all. Remaining unknown: why in-sandbox device-identity persistence fails — the root of the device-table churn.
Description
What happened: When an agent turn runs inside the sandbox gateway daemon (dashboard chat, POST /v1/chat/completions, gateway-dispatched openclaw agent) and invokes a channel tool (e.g. the message tool → Discord), the embedded tool bridge dials back to the gateway at env OPENCLAW_GATEWAY_URL (ws://10.200.0.2:18789). The gateway rejects that connection with 1008 pairing required: device is not approved yet, so every channel tool call on the warm daemon path fails, and each attempt enqueues a new unapprovable gateway-client pairing request with a fresh device id. The same token authorizes REST POST /tools/invoke-style requests directly with HTTP 200, and the connect frame demonstrably contains that token — the rejection comes from the device-pairing locality gate, not from missing credentials.
What I expected: The daemon's own dial-back, carrying the correct shared gateway token to the sandbox's own gateway address, should be treated like the loopback backend connection it logically is (OpenClaw exempts local backend gateway-client connections with shared-secret auth from device pairing), and the channel tool should execute. Practical impact: delegated channel actions are forced onto a cold openclaw agent --local CLI workaround (~25 s per action) instead of the warm daemon path (~7 s).
Prior art — same failure class, already fixed for CLI contexts:#6645 (WhatsApp login; building on #6413/#4462) fixed this exact locality behavior for exec-context CLI flows — the in-tree comment in scripts/nemoclaw-start.sh states "a private-IP origin makes the gateway's locality check strip operator scopes regardless of token auth", and the merged remedy is to scrub the stashed private veth URL so OpenClaw resolves the loopback gateway from its own config. The gateway daemon itself, however, inherits the private-IP URL in-process from the export OPENCLAW_GATEWAY_URL="ws://${_GATEWAY_WS_HOST}:…" in scripts/nemoclaw-start.sh, so no wrapper-side scrub reaches the embedded tool bridge — it is the remaining unscrubbed consumer. Same locality root, two symptom shapes: device-less CLI connects get scopes stripped; the device-bearing embedded bridge gets an unapprovable pairing demand.
Reproduction Steps
NemoClaw appliance with an OpenClaw sandbox and a Discord channel configured and connected (guild + bot ready).
Get the operator gateway token on the host: TOK=$(~/.local/bin/nemoclaw <sandbox> gateway-token | tail -1)
Observe {"ok":false,"error":{"type":"tool_error","message":"tool execution failed"}}; the in-sandbox /tmp/gateway.log shows a fresh pairing required (requestId …) from a 10.200.0.1 → 10.200.0.2:18789 connection; in-sandbox openclaw devices list --json shows a new pending entry clientId: "gateway-client", clientMode: "backend", scopes: ["operator.write"] with a different deviceId on every attempt.
Any /v1/chat/completions request whose agent turn invokes a channel tool fails the same way.
nemoclaw-debug.tar.gz
Investigation Summary
POST /tools/invokeor a/v1/chat/completionsturn) failstool execution failed; the in-sandbox gateway log showspairing required: device is not approved yeton a10.200.0.1 → 10.200.0.2:18789connection.connectframe carriesauth.tokenbyte-identical (SHA-256-compared) togateway.auth.token— the credential is not dropped by the reviewed dist patch or the OpenShell relay; the gateway still answersNOT_PAIREDand closes1008.isSharedSecretLoopbackLocalEquivalent(src/gateway/server/ws-connection/handshake-auth-helpers.ts:144) fails only itsisLoopbackAddress(remoteAddress)conjunct → pairing locality degrades toremote→shouldSkipLocalBackendSelfPairing(:257) stops exempting the backendgateway-client→requirePairing("not-paired")(message-handler.ts:1561).cli/openclaw-cli/openclaw-control-ui. This appliance accumulated 10,855 paired entries; the watcher's own CLI polling mints and self-approves a newcliidentity every ~13 s.OPENSHELL_SANDBOX=1-gated dist patch treating private-range peers as loopback-equivalent in that one locality check (~7.7 s warm turn); (b) the fix(whatsapp): pair login over the loopback gateway and preserve npm plugin provenance #6645-pattern env-boundary fix — launching the gateway daemon withenv -u OPENCLAW_GATEWAY_URL(extendingnemoclaw-start.sh's existing CLI scrub idiom to its three gateway launch/respawn sites) so the embedded bridge resolves config loopback — which passes the same checks on a pristine dist (~6.5 s) and mints no pairing records at all. Remaining unknown: why in-sandbox device-identity persistence fails — the root of the device-table churn.Description
What happened: When an agent turn runs inside the sandbox gateway daemon (dashboard chat,
POST /v1/chat/completions, gateway-dispatchedopenclaw agent) and invokes a channel tool (e.g. themessagetool → Discord), the embedded tool bridge dials back to the gateway atenv OPENCLAW_GATEWAY_URL(ws://10.200.0.2:18789). The gateway rejects that connection with1008 pairing required: device is not approved yet, so every channel tool call on the warm daemon path fails, and each attempt enqueues a new unapprovablegateway-clientpairing request with a fresh device id. The same token authorizes RESTPOST /tools/invoke-style requests directly with HTTP 200, and the connect frame demonstrably contains that token — the rejection comes from the device-pairing locality gate, not from missing credentials.What I expected: The daemon's own dial-back, carrying the correct shared gateway token to the sandbox's own gateway address, should be treated like the loopback backend connection it logically is (OpenClaw exempts local backend
gateway-clientconnections with shared-secret auth from device pairing), and the channel tool should execute. Practical impact: delegated channel actions are forced onto a coldopenclaw agent --localCLI workaround (~25 s per action) instead of the warm daemon path (~7 s).Prior art — same failure class, already fixed for CLI contexts: #6645 (WhatsApp login; building on #6413/#4462) fixed this exact locality behavior for exec-context CLI flows — the in-tree comment in
scripts/nemoclaw-start.shstates "a private-IP origin makes the gateway's locality check strip operator scopes regardless of token auth", and the merged remedy is to scrub the stashed private veth URL so OpenClaw resolves the loopback gateway from its own config. The gateway daemon itself, however, inherits the private-IP URL in-process from theexport OPENCLAW_GATEWAY_URL="ws://${_GATEWAY_WS_HOST}:…"inscripts/nemoclaw-start.sh, so no wrapper-side scrub reaches the embedded tool bridge — it is the remaining unscrubbed consumer. Same locality root, two symptom shapes: device-less CLI connects get scopes stripped; the device-bearing embedded bridge gets an unapprovable pairing demand.Reproduction Steps
TOK=$(~/.local/bin/nemoclaw <sandbox> gateway-token | tail -1){"ok":false,"error":{"type":"tool_error","message":"tool execution failed"}}; the in-sandbox/tmp/gateway.logshows a freshpairing required (requestId …)from a10.200.0.1 → 10.200.0.2:18789connection; in-sandboxopenclaw devices list --jsonshows a new pending entryclientId: "gateway-client", clientMode: "backend", scopes: ["operator.write"]with a differentdeviceIdon every attempt.Any
/v1/chat/completionsrequest whose agent turn invokes a channel tool fails the same way.Environment
6.8.12-1021-tegra, aarch64)nemoclaw --version; local checkout — repro and evidence gathered on the stock reviewed dist)compatible-endpoint(OpenRouter,openai/gpt-oss-120b), policy presets incl.discord; GPU passthrough enabledDebug Output
Checklist