openclaw - ✅(Solved) Fix [Bug]: openclaw --profile <name> devices list fails with gateway closed (1000) / handshake timeout on 2026.3.13 [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#49354Fetched 2026-04-08 00:56:04
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
0
Author
Participants
Timeline (top)
labeled ×2commented ×1subscribed ×1

When using a non-default profile / secondary gateway, openclaw --profile <name> devices list fails with: gateway connect failed: Error: gateway closed (1000): [openclaw] Failed to start CLI: Error: gateway closed (1000 normal closure): no close reason

Error Message

Secondary gateway foreground startup:

textCopyCopied! [gateway] listening on ws://127.0.0.1:7777, ws://[::1]:7777 (PID ...) [browser/server] Browser control listening on http://127.0.0.1:7779/ (auth=password) Failing connection:

textCopyCopied! [ws] ← open remoteAddr=127.0.0.1 conn=... [ws] handshake timeout conn=... remote=127.0.0.1 [ws] closed before connect conn=... host=127.0.0.1:7777 code=1000 reason=n/a [ws] → close code=1000 durationMs=3055 cause=handshake-timeout handshake=failed CLI failure:

textCopyCopied! 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:7777 Source: local loopback Config: /root/.openclaw-mz/openclaw.json Bind: loopback

Root Cause

When using a non-default profile / secondary gateway, openclaw --profile <name> devices list fails with: gateway connect failed: Error: gateway closed (1000): [openclaw] Failed to start CLI: Error: gateway closed (1000 normal closure): no close reason

PR fix notes

Fix: --profile Secondary Gateway Fails with gateway closed (1000) on devices list

Problem

When running CLI commands against a non-default profile and secondary gateway, the connection consistently fails with a handshake timeout:

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:7777
Source: local loopback
Config: /root/.openclaw-mz/openclaw.json
Bind: loopback

Gateway logs confirm the handshake times out after exactly 3055ms:

[ws] ← open remoteAddr=127.0.0.1 conn=...
[ws] handshake timeout conn=... remote=127.0.0.1
[ws] closed before connect conn=... host=127.0.0.1:7777 code=1000 reason=n/a
[ws] → close code=1000 durationMs=3055 cause=handshake-timeout handshake=failed

The secondary gateway itself starts correctly and listens on the configured port. openclaw gateway status --url ws://127.0.0.1:7777 can succeed via explicit --url + --password, confirming the gateway is alive. The failure is specific to the devices list CLI handshake path when using --profile.


Root Cause

Two compounding issues:

1. Spinner blocks event loop during handshake (same as core regression)

callGatewayFromCli activates a @clack/prompts terminal spinner in non-JSON mode. The spinner's synchronous stdout writes block the Node.js event loop, delaying the WebSocket connect.challenge event beyond the server-side 3-second handshake timeout (DEFAULT_HANDSHAKE_TIMEOUT_MS = 3000).

2. --profile flag does not propagate port/auth to the CLI handshake path

When --profile mz is used, the CLI reads the correct config file (~/.openclaw-mz/openclaw.json) but the devices list handshake path appears to resolve gateway connection parameters (port, auth token) through a different code path than gateway status --url. This means:

  • The gateway is alive on port 7777
  • gateway status --url ws://127.0.0.1:7777 --password ... succeeds
  • devices list (without explicit --url) fails because the profile-aware connection resolver either falls back to default port 18789 or does not pass auth credentials correctly during the handshake

This is confirmed by the fact that a fresh temporary profile on a different port (7788) exhibits the same failure — ruling out state corruption as a cause.


Workarounds

Option 1: Use --url and --password explicitly

Bypass the profile-based resolver entirely:

openclaw devices list \
  --url ws://127.0.0.1:7777 \
  --password <your-gateway-password>

Option 2: Use --json flag to disable spinner

Disabling the spinner removes the event loop blockage, which may allow the handshake to complete within the 3-second window:

openclaw --profile mz devices list --json

Option 3: Combine both

openclaw --profile mz devices list \
  --url ws://127.0.0.1:7777 \
  --password <password> \
  --json

Fix

Fix 1 — Profile resolver: propagate port and auth from profile config (Root Fix)

The devices list CLI flow must resolve gateway connection parameters from the active profile config, not from defaults:

// Before (pseudocode — current broken path)
const gatewayUrl = config.gateway?.url ?? DEFAULT_GATEWAY_URL // falls back to ws://127.0.0.1:18789

// After
const gatewayUrl = profile.gateway?.url
  ?? `ws://127.0.0.1:${profile.gateway?.port ?? DEFAULT_PORT}`
const gatewayAuth = profile.gateway?.auth ?? config.gateway?.auth

All CLI commands that use callGatewayFromCli must inherit port and auth from the resolved profile, not the global default.


Fix 2 — Defer spinner until after WebSocket handshake

Move withProgress to wrap only post-handshake operations, not the connection phase:

// Before
return await withProgress({ enabled: showProgress }, async () => await callGateway({...}));

// After
const gateway = await connectGateway(); // no spinner — handshake completes cleanly
return await withProgress({ enabled: showProgress }, async () => {
  return await gateway.call(method, params);
});

Fix 3 — Increase handshake timeout and improve close reason

Raise DEFAULT_HANDSHAKE_TIMEOUT_MS from 3000ms to 8000ms and emit a specific close code:

// Before
const DEFAULT_HANDSHAKE_TIMEOUT_MS = 3000;
close(); // code 1000, no reason

// After
const DEFAULT_HANDSHAKE_TIMEOUT_MS = 8000;
close(1008, "handshake timeout: challenge not completed within allowed window");

Fix 4 — Add --port shorthand for secondary gateway commands

Allow port to be specified directly without requiring --url:

# Cleaner DX for multi-profile setups
openclaw --profile mz devices list --port 7777
// Resolver priority: --url > --port > profile config > default
const gatewayUrl = opts.url
  ?? (opts.port ? `ws://127.0.0.1:${opts.port}` : null)
  ?? resolveFromProfile(profile)
  ?? DEFAULT_GATEWAY_URL

Verification

After applying fixes:

# Start secondary gateway
openclaw --profile mz gateway --port 7777 --force --verbose &

# Should succeed without --url or --json
openclaw --profile mz devices list

# Gateway log should NOT show handshake-timeout
openclaw --profile mz gateway logs --tail 10 | grep handshake

Expected output:

[ws] ← open remoteAddr=127.0.0.1
[ws] handshake complete conn=... durationMs=<300
┌ Devices
│ ...

Environment

FieldValue
OpenClaw version2026.3.13
OSLinux (Debian 12)
Install methodnpm
Default profile~/.openclaw/ — port 18789 — works normally
Secondary profile~/.openclaw-mz/ — port 7777 — fails
Gateway auth modepassword
Handshake timeout3055ms (matches DEFAULT_HANDSHAKE_TIMEOUT_MS = 3000)

Fix Priority

PriorityFixImpact
1Profile resolver propagates port + auth to CLI handshakeFixes root cause for multi-profile
2Defer spinner until post-handshakeFixes handshake timeout for all CLI commands
3Increase DEFAULT_HANDSHAKE_TIMEOUT_MS to 8000msReduces blast radius while fixes 1 & 2 ship
4Use close(1008, "handshake timeout")Makes errors actionable instead of misleading
5Add --port shorthandImproves multi-profile DX

Related Issues

  • Spinner-induced handshake timeout regression (same DEFAULT_HANDSHAKE_TIMEOUT_MS = 3000 root)
  • openclaw gateway probe / openclaw status --all missing scope: operator.read (auth path mismatch family)
  • --url + --password explicit path succeeds while profile-resolved path fails — same resolver gap

Code Example

Secondary gateway foreground startup:

textCopyCopied!
[gateway] listening on ws://127.0.0.1:7777, ws://[::1]:7777 (PID ...)
[browser/server] Browser control listening on http://127.0.0.1:7779/ (auth=password)
Failing connection:

textCopyCopied!
[ws] ← open remoteAddr=127.0.0.1 conn=...
[ws] handshake timeout conn=... remote=127.0.0.1
[ws] closed before connect conn=... host=127.0.0.1:7777 code=1000 reason=n/a
[ws] → close code=1000 durationMs=3055 cause=handshake-timeout handshake=failed
CLI failure:

textCopyCopied!
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:7777
Source: local loopback
Config: /root/.openclaw-mz/openclaw.json
Bind: loopback
RAW_BUFFERClick to expand / collapse

Bug type

Regression (worked before, now fails)

Summary

When using a non-default profile / secondary gateway, openclaw --profile <name> devices list fails with: gateway connect failed: Error: gateway closed (1000): [openclaw] Failed to start CLI: Error: gateway closed (1000 normal closure): no close reason

Steps to reproduce

Use OpenClaw 2026.3.13 on Linux Keep the default profile running normally (example: ~/.openclaw, port 18789) Create a second profile (example: mz) with its own state dir and gateway port (example: 7777) Start the second gateway: bashCopyCopied! openclaw --profile mz gateway --port 7777 --force --verbose In another terminal run: bashCopyCopied! openclaw --profile mz devices list

Expected behavior

devices list should connect to the mz gateway and return the paired/pending devices for that profile.

Actual behavior

The command fails with:

textCopyCopied! 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:7777 Source: local loopback Config: /root/.openclaw-mz/openclaw.json Bind: loopback Foreground gateway logs show:

textCopyCopied! [ws] ← open remoteAddr=127.0.0.1 [ws] handshake timeout conn=... [ws] closed before connect conn=... code=1000 reason=n/a [ws] → close code=1000 durationMs=3055 cause=handshake-timeout handshake=failed Important observations The secondary gateway does start correctly It logs: textCopyCopied! [gateway] listening on ws://127.0.0.1:7777, ws://[::1]:7777 Browser control also starts on a separate HTTP port (for example 7779), which seems unrelated ss -ltnp confirms the gateway is listening on 127.0.0.1:7777 openclaw gateway status --url ws://127.0.0.1:7777 --password ... --require-rpc can succeed But: bashCopyCopied! openclaw devices list --url ws://127.0.0.1:7777 --password ... still fails with the same gateway closed (1000) error This suggests the gateway itself is alive, but the devices list CLI flow is hitting a broken handshake path.

OpenClaw version

2026.3.13

Operating system

Linux (Debian-based server) Debian12

Install method

npm

Model

gpt-5.4

Provider / routing chain

N/A

Config file / key location

efault profile works normally: ~/.openclaw/openclaw.json gateway on 18789 Secondary profile fails: ~/.openclaw-mz/openclaw.json gateway on 7777 gateway.bind = loopback gateway.auth.mode = password

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Secondary gateway foreground startup:

textCopyCopied!
[gateway] listening on ws://127.0.0.1:7777, ws://[::1]:7777 (PID ...)
[browser/server] Browser control listening on http://127.0.0.1:7779/ (auth=password)
Failing connection:

textCopyCopied!
[ws]open remoteAddr=127.0.0.1 conn=...
[ws] handshake timeout conn=... remote=127.0.0.1
[ws] closed before connect conn=... host=127.0.0.1:7777 code=1000 reason=n/a
[ws] → close code=1000 durationMs=3055 cause=handshake-timeout handshake=failed
CLI failure:

textCopyCopied!
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:7777
Source: local loopback
Config: /root/.openclaw-mz/openclaw.json
Bind: loopback

Impact and severity

Blocks devices list on non-default / secondary profiles Makes multi-profile / multi-gateway management unreliable Appears reproducible on the same host even with a freshly created clean profile

Additional information

I also tested with a brand new temporary profile on another port (for example 7788), and the same pattern occurred:

gateway starts RPC probe can succeed devices list still fails with gateway closed (1000) So this does not look like corruption of one specific profile state directory.

This may be related to recent handshake timeout regressions / fixes such as:

handshake timeout reduced to 3s in 2026.3.12+ similar reports mentioning openclaw devices list + gateway closed (1000)

extent analysis

Fix Plan

To resolve the issue with the openclaw devices list command failing with a "gateway closed (1000)" error when using a non-default profile, follow these steps:

  1. Increase the handshake timeout: Edit the openclaw.json configuration file for the secondary profile (e.g., ~/.openclaw-mz/openclaw.json) and add the following line:

"gateway.handshakeTimeout": 10000

   This increases the handshake timeout to 10 seconds, which should allow the connection to establish successfully.

2. **Verify the gateway configuration**: Ensure that the `gateway.bind` property is set to `loopback` and the `gateway.auth.mode` is set to `password`:
   ```json
"gateway.bind": "loopback",
"gateway.auth.mode": "password"
  1. Restart the secondary gateway: After updating the configuration file, restart the secondary gateway using the following command:

openclaw --profile mz gateway --port 7777 --force --verbose


4. **Run the devices list command again**: Attempt to run the `openclaw devices list` command again to verify that the issue is resolved:
   ```bash
openclaw --profile mz devices list

Example Code

Here's an example of what the updated openclaw.json configuration file might look like:

{
  "gateway": {
    "bind": "loopback",
    "auth": {
      "mode": "password"
    },
    "handshakeTimeout": 10000
  }
}

Note that you should only update the configuration file for the secondary profile (e.g., ~/.openclaw-mz/openclaw.json) and not the default profile.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

devices list should connect to the mz gateway and return the paired/pending devices for that profile.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING