#disconnected (1008): pairing required

1 messages · Page 1 of 1 (latest)

cold lavaBOT
#

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

rotund shadowBOT
#

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:

  1. On the machine where the Gateway is running, run:
openclaw dashboard

That prints (and usually copies/opens) the right dashboard URL for that gateway.

  1. If the UI asks for a token, don’t trust .env yet — 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.

  1. 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 --version
  • openclaw 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 open http://<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.

rotund shadowBOT
#

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 ps shows ...:18789->18789, your tunnel is right:

    ssh -N -L 18789:127.0.0.1:18789 -i KEY user@server
    

    Then 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

  1. On the Ubuntu box, in the folder with your docker-compose.yml, run:
docker compose config --services
docker compose ps
  1. 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"
  1. 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:

  1. gateway.auth.token not found is normal in Docker
    In the Docker setup the gateway token is usually provided via .env / OPENCLAW_GATEWAY_TOKEN, not written into openclaw.json, so openclaw config get gateway.auth.token will say “path not found”.

  2. Your openclaw-cli container must NOT use ws://127.0.0.1:18789
    Inside the CLI container, 127.0.0.1 is 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:

  1. In your browser (Windows), open the dashboard again and try to connect so it creates a pending pairing request.

  2. 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"'
  1. 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"'
#
  1. 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-cli container is also a “new device”, so it also gets blocked with pairing required → you can’t run devices list/approve to 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:

  1. 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,
    },
  },
}
  1. Restart the gateway container:
cd ~/openclaw
docker compose restart openclaw-gateway
  1. Refresh your dashboard tab (still through your SSH tunnel) and paste the same OPENCLAW_GATEWAY_TOKEN from .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-cli container doesn’t have a persistent device identity, it also looks like a new device → you get stuck in pairing required everywhere.

Fast unblock (works great with SSH tunnel): use token-only auth for the Control UI

This is exactly what gateway.controlUi.allowInsecureAuth is for.

  1. 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,
    },
  },
}
  1. Restart the gateway container:
cd ~/openclaw
docker compose restart openclaw-gateway
#
  1. 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
rotund shadowBOT
#

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 grep output (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/.openclaw
  • OPENCLAW_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)

  1. 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
  1. 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"
'
  1. Now edit the real file on the host (root-owned):
sudo nano /root/.openclaw/openclaw.json

Make sure it contains:

#
{
  gateway: {
    controlUi: {
      allowInsecureAuth: true,
    },
  },
}
  1. Restart:
cd ~/openclaw
docker compose restart openclaw-gateway

Why the sudo-user detail matters