#Gateway closed error

1 messages · Page 1 of 1 (latest)

rose timber
#

Keep getting this error when using openclaw command.
gateway connect failed: Error: gateway closed (1000):
â—‡
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

But gateway is running

calm auroraBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

obsidian hazelBOT
#

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 target
  • Listening
rose timber
#

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.

obsidian hazelBOT
#

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:

rose timber
#

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)

obsidian hazelBOT
#

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
rose timber
#

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?

obsidian hazelBOT
#

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
rose timber
#

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

obsidian hazelBOT
#

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:

  1. Best: set a token you choose and store it in your password manager.
  2. 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).

rose timber
#

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?

obsidian hazelBOT
#

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)

  1. Start gateway in foreground (keep this terminal open):
openclaw gateway run
  1. In a second terminal, verify with explicit token:
#
openclaw gateway probe --url ws://127.0.0.1:18789 --token "<YOUR_TOKEN>"
  1. 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:

rose timber
#

I do openclaw gateway stop then do openclaw gateway run but still showing gateway running in the port?

obsidian hazelBOT
#

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.

rose timber
#

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)

obsidian hazelBOT
#

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.

rose timber
#

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)

obsidian hazelBOT
#

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/restart won’t manage foreground processes
  • you need one terminal running openclaw gateway run

Ref: https://docs.openclaw.ai/help/troubleshooting

rose timber
#

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 hazelBOT
#

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
rose timber
#

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

obsidian hazelBOT
#

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

rose timber
#

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
    }
obsidian hazelBOT
#

This is a really useful datapoint:

  • /healthz on 19001 is 200 OK âś…
  • WS probe to ws://127.0.0.1:19001 still 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.

rose timber
#

what about i go back to 18789? My vps and docker explicit open 18789

obsidian hazelBOT
#

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.

rose timber
#

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
}
]
}

obsidian hazelBOT
#

Great, this narrows it down a lot:

  • HTTP is alive (/healthz = OK)
  • WS handshake still timing out

That usually means one of these:

  1. proxy env vars are intercepting WS, or
  2. 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.

rose timber
#

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
}
]
}

obsidian hazelBOT
#

Thanks — this is now very likely a loopback WS bug/path issue in your docker setup (not token, not proxy, not config syntax):

  • HTTP /healthz works
  • WS to 127.0.0.1 times 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

GitHub

Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞 - openclaw/openclaw

rose timber
#

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
obsidian hazelBOT
#

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
rose timber
#

How do i use wws:// or ssh tunnel? Lets make it safe

obsidian hazelBOT
#

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


rose timber
#

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

obsidian hazelBOT
#

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.

GitHub

Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞 - openclaw/openclaw

rose timber
#

Is ti a bug or just my setup issue?

obsidian hazelBOT
#

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 healthz returning OK and normal usage working, but devices list still failing with gateway 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.

rose timber
#

You post it or i post?

obsidian hazelBOT
#

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

GitHub

Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞 - openclaw/openclaw

rose timber
#

But i can just ignore it right?