#Gateway closed error
1 messages · Page 1 of 1 (latest)
Yep — this usually means the service supervisor is up, but the WS RPC endpoint isn’t healthy/usable (or you’re hitting the wrong service/config).
Can you run these and paste output?
openclaw --version
openclaw gateway status
openclaw status --all
openclaw config get gateway.bind
openclaw config get gateway.auth.mode
openclaw logs --follow
What I’m looking for specifically in gateway status:
Probe targetListening
Config (cli)vsConfig (service)Last gateway error
Given your output shows Bind: lan, common causes are:
- LAN bind without valid auth token/password
- Config mismatch (CLI reads one config, service runs another)
If Config (cli) and Config (service) differ, fix with:
openclaw gateway install --force
openclaw gateway restart
Docs:
Paste the command outputs and I’ll pinpoint the exact fix.
OpenClaw 2026.3.13 (61d171a)
Service: systemd (disabled)
File logs: /tmp/openclaw/openclaw-2026-03-22.log
Service config looks out of date or non-standard.
Service config issue: Gateway service PATH is not set; the daemon should use a minimal PATH.
Recommendation: run "openclaw doctor" (or "openclaw doctor --repair").
Config (cli): ~/.openclaw/openclaw.json
Config (service): ~/.openclaw/openclaw.json
Gateway: bind=lan (0.0.0.0), port=18789 (env/config)
Probe target: ws://127.0.0.1:18789
Dashboard: http://172.17.0.3:18789/
Probe note: bind=lan listens on 0.0.0.0 (all interfaces); probing via 127.0.0.1.
Runtime: unknown (systemctl not available; systemd user services are required on Linux.)
RPC probe: ok
systemd user services unavailable.
systemd user services are unavailable; install/enable systemd or run the gateway under your supervisor.
If you're in a container, run the gateway in the foreground instead of openclaw gateway.
Troubles: run openclaw status
Troubleshooting: https://docs.openclaw.ai/troubleshooting
Gateway │ local · ws://127.0.0.1:18789 (local loopback) · unreachable (missing scope: operator.read) Diagnosis (read-only)
Gateway connection details:
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /root/.openclaw/openclaw.json
Bind: lan
âś“ Config: /root/.openclaw/openclaw.json
âś“ Restart sentinel: none
âś“ Port 18789
! Tailscale: off · unknown
error: Error: spawn tailscale ENOENT
✓ Skills: 11 eligible · 0 missing · /root/.openclaw/workspace
! Channel issues skipped (gateway unreachable)
Gateway health:
missing scope: operator.read
Pasteable debug report. Auth tokens redacted.
Troubleshooting: https://docs.openclaw.ai/troubleshooting
gateway connect failed: Error: gateway closed (1000):
Gateway not reachable. Is it running and accessible?
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /root/.openclaw/openclaw.json
Bind: lan
Hint: run openclaw doctor.
Nice, this output is super helpful — your gateway is actually up.
The real issue is auth scope, not process liveness:
missing scope: operator.read
So the client can connect, but the token/device identity it’s using doesn’t have the read scope needed for status/health RPC.
Try this sequence:
openclaw doctor --repair
openclaw devices list
openclaw devices approve --latest
If it still says missing scope: operator.read, rotate the operator token for your CLI device:
openclaw devices rotate --device <deviceId> --role operator --scope operator.read --scope operator.write
Then retry:
openclaw status
openclaw gateway probe
Also: systemd user services unavailable is expected in many container setups — run gateway foreground there (openclaw gateway run), that part is separate from your scope error.
Refs:
openclaw devices list and openclaw devices approve --latest both return
🦞 OpenClaw 2026.3.13 (61d171a) — Your AI assistant, now without the $3,499 headset.
│
gateway connect failed: Error: gateway closed (1000):
â—‡
[openclaw] Failed to start CLI: Error: gateway closed (1000 normal closure): no close reason
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /root/.openclaw/openclaw.json
Bind: lan
at Object.onClose (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60780:10)
at WebSocket.<anonymous> (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:59871:23)
at WebSocket.emit (node:events:508:28)
at WebSocket.emitClose (/root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/node_modules/ws/lib/websocket.js:273:10)
at Socket.socketOnClose (/root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/node_modules/ws/lib/websocket.js:1346:15)
at Socket.emit (node:events:508:28)
at TCP.<anonymous> (node:net:346:12)
Got it — thanks, this confirms devices can’t even complete the initial WS handshake.
At this point, do this exact isolation flow:
1) Bypass cached device auth (force shared token auth)
openclaw config get gateway.auth.token
If empty:
openclaw doctor --generate-gateway-token
openclaw config get gateway.auth.token
Then run with explicit token:
openclaw gateway probe --url ws://127.0.0.1:18789 --token "<PASTE_TOKEN>" --json
openclaw devices list --url ws://127.0.0.1:18789 --token "<PASTE_TOKEN>"
2) If explicit-token works, clear stale local device-auth cache
mv ~/.openclaw/identity/device-auth.json ~/.openclaw/identity/device-auth.json.bak
Then retry normal commands (openclaw status, openclaw devices list).
3) If it still fails, verify what process is actually on 18789
ss -ltnp | grep 18789
curl -sS http://127.0.0.1:18789/healthz
I suspect stale local device auth or wrong listener on 18789.
Paste outputs from steps 1 + 3 and I’ll give you the exact next command.
Refs:
openclaw config get gateway.auth.token
🦞 OpenClaw 2026.3.13 (61d171a) — Less clicking, more shipping, fewer "where did that file go" moments.
OPENCLAW_REDACTED
is this expected?
Yes — that is expected. __OPENCLAW_REDACTED__ means the CLI is masking secrets on output.
Since it won’t print the token, easiest path is to set a fresh token you know, then test with it explicitly:
TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.mode token
openclaw config set gateway.auth.token "$TOKEN"
openclaw config set gateway.bind loopback
Then start gateway in foreground (since systemd user services aren’t available there):
openclaw gateway run
In another shell:
openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json
openclaw devices list --url ws://127.0.0.1:18789 --token "$TOKEN"
Don’t paste the token in Discord.
Docs:
do i need to store that token for my own or just set is enough? If need to store how do i get that token
Great question.
Short answer: setting it is enough for normal local CLI use.
When you run:
openclaw config set gateway.auth.token "$TOKEN"
it is saved in your config and reused after restart.
You only need to manually keep/copy the token if you want to connect other clients (remote UI, explicit --token, etc.).
Because config get redacts secrets, to “get it again” later you have 2 options:
- Best: set a token you choose and store it in your password manager.
- Rotate to a new one anytime:
openclaw config set gateway.auth.token "$(openssl rand -hex 32)"
If you need a one-time connect URL for UI, use:
openclaw dashboard --no-open
(and copy locally; don’t post token in Discord).
openclaw gateway restart
🦞 OpenClaw 2026.3.13 (61d171a) — I can run local, remote, or purely on vibes—results may vary with DNS.
11:56:16 [restart] lsof failed during initial stale-pid scan for port 18789: ENOENT
Gateway service disabled.
Start with: openclaw gateway install
Start with: openclaw gateway
Start with: systemctl --user start openclaw-gateway.service
Start with: systemd user services are unavailable; install/enable systemd or run the gateway under your supervisor.
Start with: If you're in a container, run the gateway in the foreground instead of openclaw gateway.
root@027ef8865ea7:/# openclaw gateway stop
I cant restart the gateway, but agent can restart for me, why? And what do i need to do after setting the token?
You’re in a container, so this is expected behavior.
openclaw gateway restart controls a service manager (systemd/launchd).
Your output shows service mode is unavailable/disabled, so there is nothing to “restart” that way.
Why it felt like “agent can restart”
It can suggest/run commands in its own context, but your gateway lifecycle still depends on your host/container setup. In your container, you need foreground mode.
What to do now (after setting token)
- Start gateway in foreground (keep this terminal open):
openclaw gateway run
- In a second terminal, verify with explicit token:
openclaw gateway probe --url ws://127.0.0.1:18789 --token "<YOUR_TOKEN>"
- If that works, either:
- keep using
--token, or - export once per shell:
export OPENCLAW_GATEWAY_TOKEN="<YOUR_TOKEN>"
then run normal commands:
openclaw status
openclaw devices list
If you lost the token, just rotate to a new one:
openclaw config set gateway.auth.token "$(openssl rand -hex 32)"
Docs:
I do openclaw gateway stop then do openclaw gateway run but still showing gateway running in the port?
Yep — that’s likely an old/manual process still bound to 18789.
openclaw gateway stop only stops a managed service.
In your container, service mode is unavailable, so it won’t kill a foreground/manual process.
Use this:
ss -ltnp | grep ':18789'
You’ll get a PID. Inspect it:
ps -fp <PID>
If it’s an old OpenClaw process, stop it:
kill <PID>
# if needed:
kill -9 <PID>
Then start clean (single instance):
openclaw gateway --force
(If your install prefers run, use openclaw gateway run --force.)
Then verify with token:
openclaw gateway probe --url ws://127.0.0.1:18789 --token "<YOUR_TOKEN>"
So yes, what you’re seeing is expected when a stale manual process is still listening.
openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json
openclaw devices list --url ws://127.0.0.1:18789 --token "$TOKEN"
shows
[openclaw] Failed to start CLI: Error: gateway url override requires explicit credentials
Fix: pass --token or --password (or gatewayToken in tools).
Config: /root/.openclaw/openclaw.json
at ensureExplicitGatewayAuth (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60380:8)
at callGatewayWithScopes (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60795:2)
at async callGatewayCli (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60830:9)
at async callGateway (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60839:97)
at async file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/devices-cli-CUU7n1Kz.js:40:16
at async withProgress (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:163837:10)
at async listPairingWithFallback (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/devices-cli-CUU7n1Kz.js:74:33)
at async Command.<anonymous> (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/devices-cli-CUU7n1Kz.js:131:16)
at async Command.parseAsync (/root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/node_modules/commander/lib/command.js:1122:5)
at async runCli (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/run-main-Cu0soMcD.js:354:3)
Perfect, this error is actually good signal:
gateway url override requires explicit credentials
That means --token "$TOKEN" expanded to empty (your TOKEN var is not set in that shell).
Do this in one shell, exactly:
TOKEN="$(openssl rand -hex 32)"
openclaw config set gateway.auth.mode token
openclaw config set gateway.auth.token "$TOKEN"
echo "TOKEN length: ${#TOKEN}" # should print 64
openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json
openclaw devices list --url ws://127.0.0.1:18789 --token "$TOKEN"
If TOKEN length is 0, you found the issue.
Also: shell vars are per-terminal.
If you open a new terminal, TOKEN is gone unless you set it again.
Token lenght is 64, for the last 2 line of command return
{
"ok": false,
"degraded": false,
"ts": 1774181554784,
"durationMs": 6652,
"timeoutMs": 3000,
"primaryTargetId": null,
"warnings": [],
"network": {
"localLoopbackUrl": "ws://127.0.0.1:18789",
"localTailnetUrl": null,
"tailnetIPv4": null
},
"discovery": {
"timeoutMs": 1200,
"count": 0,
"beacons": []
},
"targets": [
{
"id": "explicit",
"kind": "explicit",
"url": "ws://127.0.0.1:18789",
"active": true,
"tunnel": null,
"connect": {
"ok": false,
"rpcOk": false,
"scopeLimited": false,
"latencyMs": null,
"error": "connect failed: connect ECONNREFUSED 127.0.0.1:18789",
"close": {
"code": 1006,
"reason": ""
}
},
"self": null,
"config": null,
"health": null,
"summary": null,
"presence": null
}
]
}
🦞 OpenClaw 2026.3.13 (61d171a) — I speak fluent bash, mild sarcasm, and aggressive tab-completion energy.
│
gateway connect failed: Error: gateway client stopped
â—‡
[openclaw] Failed to start CLI: Error: gateway timeout after 10000ms
Gateway target: ws://127.0.0.1:18789
Source: cli --url
Config: /root/.openclaw/openclaw.json
at Timeout.<anonymous> (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60786:9)
at listOnTimeout (node:internal/timers:605:17)
at process.processTimers (node:internal/timers:541:7)
Perfect debug result — this is now clear:
ECONNREFUSED 127.0.0.1:18789 = nothing is listening on that port.
So token/auth is no longer the blocker.
Next step (exact)
Run this in terminal A:
ss -ltnp | grep ':18789' || echo "nothing listening on 18789"
openclaw gateway run --verbose
Keep it running.
Then in terminal B:
openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json
If terminal A exits immediately, paste the first error lines from that terminal — that will be the real root cause (config/runtime crash on startup).
Given you’re in a container, this is expected:
gateway stop/restartwon’t manage foreground processes- you need one terminal running
openclaw gateway run
ok now openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json no return error, just a json file
{
"ok": false,
"degraded": false,
"ts": 1774181847002,
"durationMs": 5408,
"timeoutMs": 3000,
"primaryTargetId": null,
"warnings": [],
"network": {
"localLoopbackUrl": "ws://127.0.0.1:18789",
"localTailnetUrl": null,
"tailnetIPv4": null
},
"discovery": {
"timeoutMs": 1200,
"count": 0,
"beacons": []
},
"targets": [
{
"id": "explicit",
"kind": "explicit",
"url": "ws://127.0.0.1:18789",
"active": true,
"tunnel": null,
"connect": {
"ok": false,
"rpcOk": false,
"scopeLimited": false,
"latencyMs": null,
"error": "timeout",
"close": null
},
"self": null,
"config": null,
"health": null,
"summary": null,
"presence": null
}
]
}
@obsidian hazel
Nice update — this changed from ECONNREFUSED to timeout, which means:
- something is now reachable on
127.0.0.1:18789, - but it is not responding like a healthy OpenClaw gateway.
Most likely: wrong process on that port, or gateway stuck during startup.
Fastest isolate (use a clean port)
In terminal A:
openclaw gateway run --port 19001 --verbose
In terminal B:
openclaw gateway probe --url ws://127.0.0.1:19001 --token "$TOKEN" --json
If 19001 works, then port 18789 is occupied by something stale/wrong.
Also run this and paste output:
ss -ltnp | grep ':18789'
curl -sv http://127.0.0.1:18789/healthz --max-time 3
That will tell us exactly what is on 18789.
ss -ltnp | grep ':18789'
curl -sv http://127.0.0.1:18789/healthz --max-time 3
- Trying 127.0.0.1:18789...
- connect to 127.0.0.1 port 18789 from 127.0.0.1 port 60056 failed: Connection refused
- Failed to connect to 127.0.0.1 port 18789 after 0 ms: Couldn't connect to server
- Closing connection
openclaw gateway probe --url ws://127.0.0.1:19001 --token "$TOKEN" --json
{
"ok": false,
"degraded": false,
"ts": 1774182521559,
"durationMs": 4878,
"timeoutMs": 3000,
"primaryTargetId": null,
"warnings": [],
"network": {
"localLoopbackUrl": "ws://127.0.0.1:18789",
"localTailnetUrl": null,
"tailnetIPv4": null
},
"discovery": {
"timeoutMs": 1200,
"count": 0,
"beacons": []
},
"targets": [
{
"id": "explicit",
"kind": "explicit",
"url": "ws://127.0.0.1:19001",
"active": true,
"tunnel": null,
"connect": {
"ok": false,
"rpcOk": false,
"scopeLimited": false,
"latencyMs": null,
"error": "timeout",
"close": null
},
"self": null,
"config": null,
"health": null,
"summary": null,
"presence": null
},
{
"id": "localLoopback",
"kind": "localLoopback",
"url": "ws://127.0.0.1:18789",
"active": true,
"tunnel": null,
"connect": {
"ok": false,
"rpcOk": false,
"scopeLimited": false,
"latencyMs": null,
"error": "connect failed: connect ECONNREFUSED 127.0.0.1:18789",
"close": {
"code": 1006,
"reason": ""
}
},
"self": null,
"config": null,
"health": null,
"summary": null,
"presence": null
}
]
}
Guess its not 19009 fault? I am running this in a docker in vps
Yep — this is not a token problem anymore.
From your output:
18789=ECONNREFUSED→ nothing listening there.19001=timeout→ something accepted TCP but never completed Gateway WS handshake.
So the gateway process is likely starting but hanging/breaking during init.
Please run this exact 3-step check:
openclaw config validate --json
openclaw gateway run --port 19001 --bind loopback --verbose
(leave this running, paste what it prints)
In another shell while it runs:
curl -sv http://127.0.0.1:19001/healthz --max-time 3
openclaw gateway probe --url ws://127.0.0.1:19001 --token "$TOKEN" --json
If /healthz times out too, gateway init is wedged/crashing (config/plugin issue), not networking.
Since you’re in Docker on a VPS, that’s the right approach: run foreground and debug from logs first.
Docs: https://docs.openclaw.ai/help/troubleshooting
openclaw config validate --json
{"valid":true,"path":"/root/.openclaw/openclaw.json"}
- Trying 127.0.0.1:19001...
- Connected to 127.0.0.1 (127.0.0.1) port 19001
GET /healthz HTTP/1.1
Host: 127.0.0.1:19001
User-Agent: curl/8.5.0
Accept: /
< HTTP/1.1 200 OK
< X-Content-Type-Options: nosniff
< Referrer-Policy: no-referrer
< Permissions-Policy: camera=(), microphone=(), geolocation=()
< Content-Type: application/json; charset=utf-8
< Cache-Control: no-store
< Date: Sun, 22 Mar 2026 12:32:08 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Content-Length: 27
<
- Connection #0 to host 127.0.0.1 left intact
{"ok":true,"status":"live"}{
"ok": false,
"degraded": false,
"ts": 1774182739818,
"durationMs": 5722,
"timeoutMs": 3000,
"primaryTargetId": null,
"warnings": [],
"network": {
"localLoopbackUrl": "ws://127.0.0.1:18789",
"localTailnetUrl": null,
"tailnetIPv4": null
},
"discovery": {
"timeoutMs": 1200,
"count": 0,
"beacons": []
},
"targets": [
{
"id": "explicit",
"kind": "explicit",
"url": "ws://127.0.0.1:19001",
"active": true,
"tunnel": null,
"connect": {
"ok": false,
"rpcOk": false,
"scopeLimited": false,
"latencyMs": null,
"error": "timeout",
"close": null
},
"self": null,
"config": null,
"health": null,
"summary": null,
"presence": null
},
{
"id": "localLoopback",
"kind": "localLoopback",
"url": "ws://127.0.0.1:18789",
"active": true,
"tunnel": null,
"connect": {
"ok": false,
"rpcOk": false,
"scopeLimited": false,
"latencyMs": null,
"error": "connect failed: connect ECONNREFUSED 127.0.0.1:18789",
"close": {
"code": 1006,
"reason": ""
}
},
"self": null,
"config": null,
"health": null,
"summary": null,
"presence": null
}
This is a really useful datapoint:
/healthzon19001is 200 OK âś…- WS probe to
ws://127.0.0.1:19001still times out ❌
So gateway is alive, but your WebSocket client path is getting blocked/intercepted — most commonly by proxy env vars in VPS/docker shells.
Try this in the same shell where you run probe:
env | grep -iE 'http_proxy|https_proxy|all_proxy|no_proxy'
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY http_proxy https_proxy all_proxy
export NO_PROXY=127.0.0.1,localhost
export no_proxy=127.0.0.1,localhost
openclaw gateway probe --url ws://127.0.0.1:19001 --token "$TOKEN" --json
openclaw devices list --url ws://127.0.0.1:19001 --token "$TOKEN"
If this fixes it, persist NO_PROXY (or remove proxy vars) in your container env.
You’re very close — this no longer looks like config/token syntax, it looks like networking/env interception.
what about i go back to 18789? My vps and docker explicit open 18789
Yep, you can absolutely go back to 18789.
19001 was just a clean debug port to isolate the issue.
For normal setup on VPS/docker, use:
openclaw config set gateway.port 18789 --strict-json
openclaw config set gateway.bind lan
openclaw gateway run
Then verify:
curl -sS http://127.0.0.1:18789/healthz
openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json
If you’re in Docker, also make sure port mapping is there (-p 18789:18789 / compose equivalent).
One important note: even on 18789, keep NO_PROXY=127.0.0.1,localhost (or unset proxy vars) in that shell/container, otherwise WS can still time out.
curl -sS http://127.0.0.1:18789/healthz
{"ok":true,"status":"live"openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json
{
"ok": false,
"degraded": false,
"ts": 1774184409307,
"durationMs": 9101,
"timeoutMs": 3000,
"primaryTargetId": null,
"warnings": [],
"network": {
"localLoopbackUrl": "ws://127.0.0.1:18789",
"localTailnetUrl": null,
"tailnetIPv4": null
},
"discovery": {
"timeoutMs": 1200,
"count": 0,
"beacons": []
},
"targets": [
{
"id": "explicit",
"kind": "explicit",
"url": "ws://127.0.0.1:18789",
"active": true,
"tunnel": null,
"connect": {
"ok": false,
"rpcOk": false,
"scopeLimited": false,
"latencyMs": null,
"error": "timeout",
"close": null
},
"self": null,
"config": null,
"health": null,
"summary": null,
"presence": null
}
]
}
Great, this narrows it down a lot:
- HTTP is alive (
/healthz= OK) - WS handshake still timing out
That usually means one of these:
- proxy env vars are intercepting WS, or
- loopback auth/device-identity path is tripping up.
Run these exact tests:
env | grep -iE 'http_proxy|https_proxy|all_proxy|no_proxy'
NO_PROXY=127.0.0.1,localhost,172.17.0.3 no_proxy=127.0.0.1,localhost,172.17.0.3 \
HTTP_PROXY= HTTPS_PROXY= ALL_PROXY= http_proxy= https_proxy= all_proxy= \
openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json
Then test non-loopback in-container IP:
NO_PROXY=127.0.0.1,localhost,172.17.0.3 no_proxy=127.0.0.1,localhost,172.17.0.3 \
HTTP_PROXY= HTTPS_PROXY= ALL_PROXY= http_proxy= https_proxy= all_proxy= \
openclaw gateway probe --url ws://172.17.0.3:18789 --token "$TOKEN" --json
If one of those works, we’ve found the culprit (proxy/loopback path) and can lock in a permanent fix.
You’re not crazy — your gateway is up; this is specifically WS connect-path behavior.
env | grep -iE 'http_proxy|https_proxy|all_proxy|no_proxy'
NO_PROXY=127.0.0.1,localhost,172.17.0.3 no_proxy=127.0.0.1,localhost,172.17.0.3
HTTP_PROXY= HTTPS_PROXY= ALL_PROXY= http_proxy= https_proxy= all_proxy=
openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json
{
"ok": false,
"degraded": false,
"ts": 1774184814324,
"durationMs": 4939,
"timeoutMs": 3000,
"primaryTargetId": null,
"warnings": [],
"network": {
"localLoopbackUrl": "ws://127.0.0.1:18789",
"localTailnetUrl": null,
"tailnetIPv4": null
},
"discovery": {
"timeoutMs": 1200,
"count": 0,
"beacons": []
},
"targets": [
{
"id": "explicit",
"kind": "explicit",
"url": "ws://127.0.0.1:18789",
"active": true,
"tunnel": null,
"connect": {
"ok": false,
"rpcOk": false,
"scopeLimited": false,
"latencyMs": null,
"error": "timeout",
"close": null
},
"self": null,
"config": null,
"health": null,
"summary": null,
"presence": null
}
]
}
Thanks — this is now very likely a loopback WS bug/path issue in your docker setup (not token, not proxy, not config syntax):
- HTTP
/healthzworks - WS to
127.0.0.1times out
Let’s use a practical workaround with the container IP instead of loopback.
Run:
openclaw gateway probe --url ws://172.17.0.3:18789 --token "$TOKEN" --json
openclaw devices list --url ws://172.17.0.3:18789 --token "$TOKEN"
If that works, set these for your shell/session:
export OPENCLAW_GATEWAY_URL="ws://172.17.0.3:18789"
export OPENCLAW_GATEWAY_TOKEN="$TOKEN"
Then run normal commands (openclaw status, openclaw devices list, etc.).
At this point you also have enough evidence for a bug report (loopback HTTP live + WS timeout).
Please file it here with the logs/output you already pasted:
https://github.com/openclaw/openclaw/issues
Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞 - openclaw/openclaw
openclaw] Failed to start CLI: Error: SECURITY ERROR: Gateway URL "ws://172.17.0.3:18789" uses plaintext ws:// to a non-loopback address.
Both credentials and chat data would be exposed to network interception.
Source: cli --url
Config: /root/.openclaw/openclaw.json
Fix: Use wss:// for remote gateway URLs.
Safe remote access defaults:
- keep gateway.bind=loopback and use an SSH tunnel (ssh -N -L 18789:127.0.0.1:18789 user@gateway-host)
- or use Tailscale Serve/Funnel for HTTPS remote access
Break-glass (trusted private networks only): set OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1
Doctor: openclaw doctor --fix
Docs: https://docs.openclaw.ai/gateway/remote
at buildGatewayConnectionDetails (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60402:60)
at callGatewayWithScopes (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60804:28)
at async callGatewayCli (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60830:9)
at async callGateway (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60839:97)
at async file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/devices-cli-CUU7n1Kz.js:40:16
at async withProgress (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:163837:10)
at async listPairingWithFallback (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/devices-cli-CUU7n1Kz.js:74:33)
at async Command.<anonymous> (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/devices-cli-CUU7n1Kz.js:131:16)
at async Command.parseAsync (/root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/node_modules/commander/lib/command.js:1122:5)
at async runCli (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/run-main-Cu0soMcD.js:354:3
Yep — that error is expected now. You hit OpenClaw’s security guardrail:
ws://+ non-loopback IP (172.17.0.3) is blocked by default.
For your private Docker network, use the documented break-glass env var:
export OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1
openclaw gateway probe --url ws://172.17.0.3:18789 --token "$TOKEN" --json
openclaw devices list --url ws://172.17.0.3:18789 --token "$TOKEN"
If this works, you’ve confirmed the loopback path is the issue and this is a viable private-network workaround.
For long-term safer setup, use one of:
wss://(TLS)- SSH tunnel to loopback
- Tailscale Serve/Funnel
How do i use wws:// or ssh tunnel? Lets make it safe
Great call — and quick typo fix: it’s wss:// (not wws://).
Easiest safe setup (recommended): SSH tunnel
Use plain ws://127.0.0.1 through SSH (encrypted by SSH).
1) On VPS/docker host
Expose gateway only to host loopback (not public internet).
If using Docker run/compose, bind published port to localhost:
- run style:
-p 127.0.0.1:18789:18789
2) From your local machine
ssh -N -L 18789:127.0.0.1:18789 <user>@<vps-ip-or-host>
Keep this terminal open.
3) Connect via tunnel
Now use:
- URL:
ws://127.0.0.1:18789 - token: your gateway token
Example probe:
openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json
Alternative: true wss://
Put Caddy/Nginx in front with TLS certs and proxy to local gateway, then use:
wss://your-domain:443
After this, you can remove the break-glass env var (OPENCLAW_ALLOW_INSECURE_PRIVATE_WS).
Yes im actually already using the ssh tunnel. Now the thing is just openclaw devices list shows
openclaw devices list
🦞 OpenClaw 2026.3.13 (61d171a) — I'm like tmux: confusing at first, then suddenly you can't live without me.
│
gateway connect failed: Error: gateway closed (1000):
â—‡
[openclaw] Failed to start CLI: Error: gateway closed (1000 normal closure): no close reason
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /root/.openclaw/openclaw.json
Bind: lan
at Object.onClose (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:60780:10)
at WebSocket.<anonymous> (file:///root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/dist/auth-profiles-DRjqKE3G.js:59871:23)
at WebSocket.emit (node:events:508:28)
at WebSocket.emitClose (/root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/node_modules/ws/lib/websocket.js:273:10)
at Socket.socketOnClose (/root/.nvm/versions/node/v24.14.0/lib/node_modules/openclaw/node_modules/ws/lib/websocket.js:1346:15)
at Socket.emit (node:events:508:28)
at TCP.<anonymous> (node:net:346:12)
but currently doesnt affect anything
Yeah, that makes sense — if everything else works, this is likely a devices list path bug (or edge-case auth handshake) in 2026.3.13, not a full gateway outage.
So:
- âś… Safe to keep using OpenClaw if your normal flows are fine.
- ⚠️ You’ll only feel it when using
devices *commands.
If you want to clean it up later, I’d file this as a bug with the exact outputs you already collected (super useful repro):
https://github.com/openclaw/openclaw/issues
If you want, I can help you draft the issue body in one paste-ready block.
Is ti a bug or just my setup issue?
Short answer: probably both, but the remaining part looks like a bug.
- Early on, there were setup issues (service mode in container, token handling, port confusion).
- Now, with
healthzreturning OK and normal usage working, butdevices liststill failing withgateway closed (1000)/timeouts, that strongly looks like a setup-specific bug/edge case (docker + SSH tunnel + loopback/auth path).
So no, this is not “you did it wrong.” You likely hit a real bug condition.
If you want, I can give you a copy-paste GitHub issue template with your exact repro steps/output so maintainers can reproduce fast.
You post it or i post?
You should post it — I can’t open GitHub issues directly from here.
If you want, paste this:
Title
devices list fails with gateway closed (1000)/timeout while gateway healthz is OK (Docker + SSH tunnel)
Body
## Version
OpenClaw 2026.3.13 (61d171a)
## Environment
- VPS + Docker
- Gateway running in container
- Using SSH tunnel
- Config path: /root/.openclaw/openclaw.json
## Problem
`openclaw devices list` fails with:
- `gateway closed (1000 normal closure): no close reason`
- sometimes `gateway timeout after 10000ms`
But gateway appears alive.
## What works
- `curl http://127.0.0.1:18789/healthz` returns `{"ok":true,"status":"live"}`
- Core bot behavior works
## What fails
- `openclaw devices list`
- `openclaw gateway probe --url ws://127.0.0.1:18789 --token "$TOKEN" --json` shows explicit target timeout
## Extra notes
- `openclaw config validate --json` => valid true
- Proxy vars were unset / NO_PROXY set during testing
- Also tested alternate port 19001 with same behavior (healthz OK, WS probe timeout)
Issue link: https://github.com/openclaw/openclaw/issues
But i can just ignore it right?