#disconnected (1008): pairing required
1 messages · Page 1 of 1 (latest)
That “disconnected (1008)” in the Control UI almost always means the WebSocket auth failed (wrong token / wrong gateway URL / you’re not actually hitting the gateway you think you are).
Here’s the quickest way to get it connected:
- On the machine where the Gateway is running, run:
openclaw dashboard
That prints (and usually copies/opens) the right dashboard URL for that gateway.
- If the UI asks for a token, don’t trust
.envyet — grab the token the Gateway is actually using:
openclaw config get gateway.auth.token
Paste that into the Control UI auth/token field and reconnect.
- If your Gateway is on a remote server/VPS, opening
http://127.0.0.1:18789/on your laptop won’t work (127.0.0.1 points to your laptop, not the server). Use an SSH tunnel:
ssh -N -L 18789:127.0.0.1:18789 user@your-server
Then open http://127.0.0.1:18789/ locally and use the same gateway.auth.token.
If you paste these, I can tell you exactly which case you’re in:
openclaw --versionopenclaw gateway status- and: are you opening the dashboard on the same machine as the gateway, or from another device?
Docs refs (relevant sections):
If you’re on Windows and you ran something like:
ssh user@server openclaw dashboard
then “command not found” can happen even when OpenClaw is installed on the server, because SSH runs a non‑login shell (your PATH setup from ~/.profile / ~/.bashrc might not load).
Try one of these:
Option A (recommended): run it in a login shell + just print the URL
From Windows:
ssh user@server "bash -lc 'openclaw dashboard --no-open'"
Option B: SSH in interactively first
ssh user@server
openclaw dashboard --no-open
If it still says “openclaw: command not found”
Then OpenClaw probably isn’t installed (or isn’t on PATH) on the machine you SSH’d into. Check:
ssh user@server "command -v openclaw || command -v clawdbot || echo NOT_FOUND"
Also: don’t paste your gateway token into Discord (that token is basically admin access).
If you paste the output of the command -v ... line (just that), and tell me whether the Gateway is running on a VPS/Linux or on your Windows machine, I’ll point you to the exact next step.
If OpenClaw is running inside Docker, then openclaw dashboard / openclaw config … need to be run inside the container, not on your Windows SSH client.
1) Get the actual token the gateway is using (inside the container)
On the machine that runs Docker:
docker ps --format "table {{.Names}}\t{{.Ports}}"
Pick the container name, then:
docker exec -it <container_name> openclaw config get gateway.auth.token
(Do not paste that token into Discord.)
You can also print the dashboard URL (mostly useful to extract the ?token=... part):
docker exec -it <container_name> openclaw dashboard --no-open
2) Make sure you’re opening the right dashboard from your laptop
You need to hit the host port that Docker published, not the container’s 127.0.0.1.
- If Docker publishes
…:18789->18789/tcp, then openhttp://<docker-host>:18789/ - If the gateway is on a remote VPS, I strongly recommend an SSH tunnel instead of exposing the dashboard:
From Windows PowerShell:
ssh -N -L 18789:127.0.0.1:18789 user@your-server
Then open locally:
http://127.0.0.1:18789/
3) Why your .env token might not work
In Docker, your host .env file often isn’t being loaded unless you explicitly pass it (compose env_file: / environment:). So the container may be using a different gateway.auth.token than what you pasted.
Tell me which setup you have (just one line):
- Docker on your Windows machine (Docker Desktop), or on a Linux VPS?
And paste the output of:
docker ps --format "table {{.Names}}\t{{.Ports}}"
(redact IPs if you want). That will tell us exactly what URL/port you should be using.
Yep — that’s expected in a Docker setup.
When OpenClaw is running inside a container, the openclaw CLI usually does not exist on the Ubuntu host, so typing openclaw after SSH’ing into the host will give command not found. You need to run the CLI inside the container (via docker exec / docker compose exec).
1) Find the gateway container / published port
On the Ubuntu host (SSH session):
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"
You’re looking for something that exposes 18789 (e.g. 0.0.0.0:18789->18789/tcp or 127.0.0.1:18789->18789/tcp).
2) Run openclaw inside the container
Replace <container_name> with the name from docker ps:
docker exec -it <container_name> openclaw --version
docker exec -it <container_name> openclaw dashboard --no-open
openclaw dashboard --no-open will print the correct dashboard URL/token for that running gateway.
3) Make sure your SSH tunnel matches the host port Docker publishes
-
If
docker psshows...:18789->18789, your tunnel is right:ssh -N -L 18789:127.0.0.1:18789 -i KEY user@serverThen open:
http://127.0.0.1:18789/ -
If it shows a different host port like
...:18888->18789, then your tunnel must use 18888 on the right side:
ssh -N -L 18789:127.0.0.1:18888 -i KEY user@server
Why your .env token didn’t work
Most likely your .env on the host isn’t actually being passed into the container (unless your docker-compose.yml uses env_file: / environment:). So the gateway is running with a different gateway.auth.token than what you pasted.
If you paste the output of:
docker ps --format "table {{.Names}}\t{{.Ports}}"
(I don’t need any tokens), I can tell you exactly what the correct tunnel target should be.
That error is actually a good clue: you’re exec’ing into the gateway container, and that container often doesn’t have an openclaw binary on $PATH.
In the Docker Compose setup, the CLI is usually exposed via a separate service called openclaw-cli.
Do this on the Ubuntu host (over SSH), from the repo folder that has docker-compose.yml
cd /path/to/your/openclaw-repo
docker compose ps
Then run the CLI via the CLI container:
docker compose run --rm openclaw-cli dashboard --no-open
To fix the exact “pairing required / 1008” thing, you typically also need to approve the browser device:
docker compose run --rm openclaw-cli devices list
docker compose run --rm openclaw-cli devices approve <requestId>
After approving, reload the Control UI tab and it should connect.
If you don’t have an openclaw-cli service
Paste (just) the output of:
cd /path/to/your/openclaw-repo
docker compose config --services
and I’ll tell you the exact command for your compose file.
(And don’t paste tokens into Discord—those are basically admin credentials.)
That output means the CLI container is trying to call the gateway at ws://127.0.0.1:18789, but inside a container 127.0.0.1 = “this CLI container”, not your gateway container → so the connection dies (1006).
Also: the CLAUDE_* env var warnings are harmless (ignore).
Fix: point the CLI at the gateway container (service DNS), not localhost
- On the Ubuntu box, in the folder with your
docker-compose.yml, run:
docker compose config --services
docker compose ps
- Your gateway service is usually named
openclaw-gateway. Then do:
TOKEN="$(docker compose run --rm openclaw-cli config get gateway.auth.token)"
docker compose run --rm openclaw-cli devices list --url ws://openclaw-gateway:18789 --token "$TOKEN"
- You should see a pending pairing request. Approve it:
docker compose run --rm openclaw-cli devices approve <requestId> --url ws://openclaw-gateway:18789 --token "$TOKEN"
Then reload the Control UI tab → the “disconnected (1008): pairing required” should go away.
If it still can’t connect
Then your gateway container is probably listening only on 127.0.0.1 inside the container (loopback), which prevents other containers from reaching it. Check:
docker compose exec openclaw-gateway ss -ltnp | grep 18789
If you see 127.0.0.1:18789, you need to change the gateway bind to LAN inside Docker (but you can still publish the port only on host loopback for safety).
If you paste the outputs of docker compose config --services + docker compose ps (no tokens), I’ll tell you the exact --url ws://... value for your compose file.
Perfect, your compose services look right (openclaw-cli + openclaw-gateway), and the gateway is only published on host loopback (127.0.0.1:18789), which is good for SSH tunneling.
Two key points:
-
gateway.auth.tokennot found is normal in Docker
In the Docker setup the gateway token is usually provided via.env/OPENCLAW_GATEWAY_TOKEN, not written intoopenclaw.json, soopenclaw config get gateway.auth.tokenwill say “path not found”. -
Your
openclaw-clicontainer must NOT usews://127.0.0.1:18789
Inside the CLI container,127.0.0.1is the CLI container itself. You need to point it at the gateway service DNS name:ws://openclaw-gateway:18789.
Run this on the Ubuntu host (in ~/openclaw)
This grabs the token from .env without printing it, then lists/approves the pairing request:
cd ~/openclaw
TOKEN="$(grep -m1 '^OPENCLAW_GATEWAY_TOKEN=' .env | cut -d= -f2-)"
docker compose run --rm openclaw-cli devices list --url ws://openclaw-gateway:18789 --token "$TOKEN"
You should see a pending request id. Approve it:
docker compose run --rm openclaw-cli devices approve <requestId> --url ws://openclaw-gateway:18789 --token "$TOKEN"
Then reload the Control UI tab (the one you opened through your SSH tunnel). The “disconnected (1008): pairing required” should be gone.
If devices list shows nothing pending, do this sequence:
- Open the Control UI and try to connect again (so it generates a pairing request),
- then rerun
devices list.
That makes sense: when you run openclaw-cli from a different container, the gateway sees it as a remote device, so it also demands pairing… which blocks devices list (chicken/egg).
The trick is: run the devices commands from inside the gateway container and connect to 127.0.0.1 from there (that counts as “local” and is auto‑approved).
From your Ubuntu SSH session in ~/openclaw:
-
In your browser (Windows), open the dashboard again and try to connect so it creates a pending pairing request.
-
Then run (inside the gateway container):
docker compose exec openclaw-gateway sh -lc 'node dist/index.js devices list --url ws://127.0.0.1:18789 --token "$OPENCLAW_GATEWAY_TOKEN"'
- You should see a
requestId. Approve it:
docker compose exec openclaw-gateway sh -lc 'node dist/index.js devices approve <requestId> --url ws://127.0.0.1:18789 --token "$OPENCLAW_GATEWAY_TOKEN"'
- Reload the Control UI tab → it should connect (no more 1008).
If devices list shows nothing pending, just try connecting in the browser once more (it only creates the request when the UI attempts the WS handshake), then rerun devices list.
You’ve basically hit a Docker + device-pairing bootstrap edge case:
- The Gateway sees your browser (via SSH tunnel → Docker port publish) as coming from a Docker bridge IP, not true localhost.
- So it demands device pairing.
- But your
openclaw-clicontainer is also a “new device”, so it also gets blocked withpairing required→ you can’t rundevices list/approveto unblock yourself.
Quick fix (recommended for SSH-tunnel setups): disable device pairing for the Control UI
This keeps token auth, but skips the device identity/pairing requirement for the web UI.
On the Ubuntu host:
- Edit (or create)
~/.openclaw/openclaw.json:
nano ~/.openclaw/openclaw.json
Add this (merge into your existing JSON5, don’t delete other config):
{
gateway: {
controlUi: {
allowInsecureAuth: true,
},
},
}
- Restart the gateway container:
cd ~/openclaw
docker compose restart openclaw-gateway
- Refresh your dashboard tab (still through your SSH tunnel) and paste the same
OPENCLAW_GATEWAY_TOKENfrom.env.
That should stop the disconnected (1008): pairing required loop.
More secure “proper” fix (later)
If you want to keep device pairing on, the clean solution is to avoid the Docker bridge weirdness (e.g. Tailscale Serve/HTTPS, or run the gateway on the host instead of in a container, or network_mode: host). But the allowInsecureAuth toggle is the quickest way to get unstuck when you’re already protected by an SSH tunnel + token.
If you try the config change and it still won’t connect, paste only:
- the first ~30 lines of
~/.openclaw/openclaw.json(redact secrets) docker compose logs --tail=50 openclaw-gateway
You’re in the repo folder (~/openclaw). The config you need is not in that directory.
For Docker installs, the Gateway reads the config from the state dir (normally on the host at ~/.openclaw/openclaw.json, and it’s bind-mounted into the container as /home/node/.openclaw/openclaw.json).
1) Check where the file is
Run on the Ubuntu host (your SSH session):
ls -la ~/.openclaw
If you see openclaw.json, open it:
nano ~/.openclaw/openclaw.json
If you don’t see it, create it:
mkdir -p ~/.openclaw
nano ~/.openclaw/openclaw.json
2) Add the setting
Put this in the file (JSON5 is fine):
{
gateway: {
controlUi: {
allowInsecureAuth: true,
},
},
}
(If the file already has content, just merge that block into the existing { ... }.)
3) Restart the gateway container
From ~/openclaw:
docker compose restart openclaw-gateway
Then refresh the dashboard in your browser (via the SSH tunnel) and connect again.
If ~/.openclaw/openclaw.json doesn’t exist but the gateway still has one inside the container, paste the output of this (no secrets):
docker compose exec openclaw-gateway ls -la /home/node/.openclaw
Yeah — that won’t affect anything. The Gateway is not reading ~/openclaw/openclaw.json (repo root). It reads config from the state dir:
- on the host:
~/.openclaw/openclaw.json - inside the container:
/home/node/.openclaw/openclaw.json
So right now your change simply isn’t being loaded.
1) Verify what the gateway is actually using
Run on the Ubuntu host:
docker compose exec openclaw-gateway ls -la /home/node/.openclaw
docker compose exec openclaw-gateway sh -lc 'test -f /home/node/.openclaw/openclaw.json && head -n 40 /home/node/.openclaw/openclaw.json || echo "NO /home/node/.openclaw/openclaw.json"'
2) Put the config in the correct place (host)
nano ~/.openclaw/openclaw.json
For a quick unblock, put this in:
{
gateway: {
controlUi: {
// break-glass: disables device identity/pairing checks for the Control UI
dangerouslyDisableDeviceAuth: true,
},
},
}
Then restart:
docker compose restart openclaw-gateway
Now your browser UI should connect with just the gateway token (no pairing loop).
3) After you’re in: re-enable normal security (recommended)
Once you’re connected and stable, flip it back to false and restart again. (Leaving it on is a security downgrade; it’s fine for debugging over an SSH tunnel, but don’t keep it long-term.)
If you paste the output of the ls -la /home/node/.openclaw command (no tokens), I can confirm whether your host ~/.openclaw is actually mounted into the container or if your compose file is using a different state path.
/home/node/.openclaw is a path inside the container, not on your Ubuntu host — so running ls /home/node/.openclaw on the host will (correctly) say “No such file”.
Right now the important bit is: you created openclaw.json in the repo folder (~/openclaw/). The gateway container is not reading that.
Step 1: find where your real OpenClaw config/state lives on the Ubuntu host
On the Ubuntu host, run:
ls -la ~/.openclaw || true
sudo ls -la /root/.openclaw || true
One of those will usually contain your real openclaw.json + state.
If neither exists, show me the volume mounts from compose:
cd ~/openclaw
sed -n '/openclaw-gateway:/,/^[^ ]/p' docker-compose.yml
Step 2 (break-glass): disable Control UI device pairing so you can get in
Edit the real config file you found above (most likely ~/.openclaw/openclaw.json, or /root/.openclaw/openclaw.json if you ran setup with sudo):
nano ~/.openclaw/openclaw.json
# or: sudo nano /root/.openclaw/openclaw.json
Add:
{
gateway: {
controlUi: {
dangerouslyDisableDeviceAuth: true,
},
},
}
Then restart the gateway container:
cd ~/openclaw
docker compose restart openclaw-gateway
Now refresh your dashboard (through the SSH tunnel) and the pairing required loop should stop.
After it works
Turn dangerouslyDisableDeviceAuth back to false once you’re connected and stable (it’s a security downgrade; fine for debugging over SSH tunnel, not great long-term).
Run those ls -la ~/.openclaw / sudo ls -la /root/.openclaw commands and paste the output (no tokens) — I’ll tell you exactly which file to edit if it’s not obvious.
The fact it still says pairing required after you edited a config file is a big hint: the gateway container is almost certainly not reading the file you edited.
Also (important): you pasted your gateway token (topsecretomer) in Discord. Assume it’s burned and rotate it later.
1) Find the config file the container is actually using
Run this on the Ubuntu host:
docker inspect openclaw-openclaw-gateway-1 --format '{{range .Mounts}}{{println .Destination " <= " .Source}}{{end}}'
You’re looking for a mount that ends with something like:
/home/node/.openclaw(or similar)
That tells us the host path you must edit.
Then verify inside the container where it expects config:
docker compose exec openclaw-gateway sh -lc 'ls -la /home/node; ls -la /home/node/.openclaw || true'
2) Unblock the Control UI (works well with SSH tunnel + Docker)
Once you’ve located the real openclaw.json (the one mounted into the container), set:
{
gateway: {
controlUi: {
allowInsecureAuth: true,
},
},
}
Restart:
docker compose restart openclaw-gateway
Then open the UI in a non-localhost origin
Because you’re using Docker port-forwarding, the gateway doesn’t see you as “local”, so it wants pairing. The trick is to make the browser not generate device identity and use token-only auth instead.
On your Windows machine (with the SSH tunnel still running), open:
http://127.0.0.1.nip.io:18789/
In the UI set:
- WebSocket URL:
ws://127.0.0.1.nip.io:18789 - Token: (your gateway token)
This usually bypasses the whole device-pairing loop.
3) Paste outputs (no secrets)
If you paste the output of the docker inspect ...Mounts... command above, I can tell you exactly which file to edit (and we can also confirm whether your config is actually being loaded).
Those env vars explain what’s going on.
- Your gateway is running in Docker with
OPENCLAW_GATEWAY_BIND=lan, so from the gateway’s point of view your browser connection is not “local” (it comes in via Docker NAT), so it enforces device pairing. - And because your
openclaw-clicontainer doesn’t have a persistent device identity, it also looks like a new device → you get stuck inpairing requiredeverywhere.
Fast unblock (works great with SSH tunnel): use token-only auth for the Control UI
This is exactly what gateway.controlUi.allowInsecureAuth is for.
- Edit the real config file the container uses (based on your env: it’s /root/.openclaw on the host):
sudo nano /root/.openclaw/openclaw.json
Put (or merge) this:
{
gateway: {
controlUi: {
allowInsecureAuth: true,
},
},
}
- Restart the gateway container:
cd ~/openclaw
docker compose restart openclaw-gateway
- On Windows (with your SSH tunnel still running), do not use
localhost. Open:
http://127.0.0.1.nip.io:18789/
In “Gateway Access” set:
- WebSocket URL:
ws://127.0.0.1.nip.io:18789 - Token: (your gateway token)
Because nip.io is not treated as “localhost secure context”, the UI can’t generate device identity, and with allowInsecureAuth: true the gateway will accept token-only and skip the pairing gate.
Important: rotate your token
You pasted OPENCLAW_GATEWAY_TOKEN into Discord, so assume it’s compromised. After you’re connected, rotate it (new random string in .env, then restart).
If this still shows pairing required, paste (no secrets) the output of:
sudo head -n 60 /root/.openclaw/openclaw.json
docker compose logs --tail=80 openclaw-gateway
That new error is actually progress: it means the gateway is not seeing gateway.controlUi.allowInsecureAuth: true yet.
When the setting is active, the UI won’t complain about “requires HTTPS or localhost (secure context)” on nip.io — it will just do token-only.
So we need to confirm you edited the actual config file mounted into the container, not a random openclaw.json in the repo.
0) Important: rotate your token
You posted OPENCLAW_GATEWAY_TOKEN here. Treat it as compromised and change it later.
1) Confirm what config file the container is using + whether it contains the setting
Run these on the Ubuntu host:
cd ~/openclaw
# show what host path is mounted into the container (so we edit the right file)
docker inspect openclaw-openclaw-gateway-1 \
--format '{{range .Mounts}}{{println .Destination " <= " .Source}}{{end}}' | sed -n '1,120p'
# confirm the config file exists INSIDE the gateway container and contains the flag
docker compose exec openclaw-gateway sh -lc '
echo "XDG_CONFIG_HOME=$XDG_CONFIG_HOME";
ls -la "$XDG_CONFIG_HOME" || true;
echo "--- grep flags ---";
grep -n "allowInsecureAuth\\|dangerouslyDisableDeviceAuth" "$XDG_CONFIG_HOME/openclaw.json" || true
'
Paste just:
- the one mount line that mentions
.openclaw - and the
grepoutput (no secrets)
2) Set the flag in the real config (wherever the mount points)
Based on your env, it’s probably host path: /root/.openclaw/openclaw.json (mounted to /home/node/.openclaw/openclaw.json).
Make sure the file contains:
{
gateway: {
controlUi: {
allowInsecureAuth: true,
},
},
}
Then restart:
docker compose restart openclaw-gateway
3) Use the nip.io URL again
Open in your Windows browser (with the SSH tunnel):
http://127.0.0.1.nip.io:18789/
and set WS URL to:
ws://127.0.0.1.nip.io:18789
If the config flag is actually loaded, the secure-context error should disappear.
If it still doesn’t, the mount/config path output will tell us exactly why (wrong file, not mounted, or being overwritten).
Yes — that can absolutely be the reason.
In your .env you posted:
OPENCLAW_CONFIG_DIR=/root/.openclawOPENCLAW_WORKSPACE_DIR=/root/.openclaw/workspace
So your Docker gateway is using root’s OpenClaw state/config. If you’re SSH’d in as a new sudo user, and you edit ~/.openclaw/openclaw.json, you’re editing your user’s home, not /root/.openclaw/.... The container won’t see it.
Do this to remove all ambiguity (no guessing)
- Check which config file is mounted into the gateway container:
docker inspect openclaw-openclaw-gateway-1 --format '{{range .Mounts}}{{println .Destination " <= " .Source}}{{end}}' | grep openclaw
- Print the actual config file the gateway sees (inside container):
docker compose exec openclaw-gateway sh -lc '
echo "XDG_CONFIG_HOME=$XDG_CONFIG_HOME";
ls -la "$XDG_CONFIG_HOME";
echo "----- openclaw.json (first 80 lines) -----";
sed -n "1,80p" "$XDG_CONFIG_HOME/openclaw.json" 2>/dev/null || echo "NO openclaw.json there"
'
- Now edit the real file on the host (root-owned):
sudo nano /root/.openclaw/openclaw.json
Make sure it contains:
{
gateway: {
controlUi: {
allowInsecureAuth: true,
},
},
}
- Restart:
cd ~/openclaw
docker compose restart openclaw-gateway