Description
When running openclaw devices list (or other CLI commands that require gateway scopes), the command fails with:
gateway connect failed: Error: gateway closed (1000): no close reason
The gateway logs show the underlying error:
[ws] ⇄ res ✗ config.get 0ms errorCode=INVALID_REQUEST errorMessage=missing scope: operator.read
Root Cause Analysis
After debugging, I found the issue is in the CLI client's device identity handling:
-
CLI connects without device identity on loopback: In auth-profiles-*.js, the function shouldAttachDeviceIdentityForGatewayCall() returns false when:
- Token auth is provided (
params.token is set)
- Connection is to localhost (
127.0.0.1, ::1, or localhost)
-
Gateway clears scopes when device identity is missing: In gateway-cli-*.js, the function handleMissingDeviceIdentity() calls clearUnboundScopes() when the client doesn't have device identity and isn't Control UI.
-
Result: CLI scopes (operator.read, operator.write, etc.) are cleared, causing subsequent API calls to fail with "missing scope: operator.read"
Steps to Reproduce
- Configure gateway with token auth:
{
"gateway": {
"port": 18789,
"mode": "local",
"bind": "loopback",
"auth": {
"mode": "token",
"token": "xxx"
}
}
}
- Run any CLI command that requires scopes:
openclaw devices list
openclaw gateway call device.pair.list --token "xxx"
openclaw gateway probe
- Observe the error:
gateway connect failed: Error: gateway closed (1000): no close reason
Expected Behavior
CLI commands should work with token auth on loopback without requiring device identity pairing, OR the CLI should always attach device identity when configured.
Current Workaround
None found. The device identity file exists at ~/.openclaw/identity/device.json and the device is paired in the gateway, but the CLI doesn't use it for loopback connections with token auth.
Suggested Fix
Option 1: Always attach device identity for CLI clients regardless of connection type:
function shouldAttachDeviceIdentityForGatewayCall(params) {
// Always attach device identity for CLI mode
if (params.mode === GATEWAY_CLIENT_MODES.CLI) return true;
if (!(params.token || params.password)) return true;
try {
const parsed = new URL(params.url);
return ![
"127.0.0.1",
"::1",
"localhost"
].includes(parsed.hostname);
} catch {
return true;
}
}
Option 2: Allow CLI clients to skip device identity check when token auth succeeds (similar to shouldSkipBackendSelfPairing for BACKEND mode):
function shouldSkipCliSelfPairing(params) {
if (!(params.connectParams.client.id === GATEWAY_CLIENT_IDS.CLI && params.connectParams.client.mode === GATEWAY_CLIENT_MODES.CLI)) return false;
const usesSharedSecretAuth = params.authMethod === "token" || params.authMethod === "password";
return params.isLocalClient && !params.hasBrowserOriginHeader && params.sharedAuthOk && usesSharedSecretAuth;
}
Environment
- OS: macOS Darwin 25.3.0
- openclaw version: 2026.3.13
- Node.js version: $(node --version)
Additional Context
The openclaw gateway probe command shows:
Warning:
- Probe diagnostics are limited by gateway scopes (missing operator.read). Connection succeeded, but status details may be incomplete. Hint: pair device identity or use credentials with operator.read.
This confirms the gateway connection succeeds but scopes are not properly granted.
Description
When running
openclaw devices list(or other CLI commands that require gateway scopes), the command fails with:The gateway logs show the underlying error:
Root Cause Analysis
After debugging, I found the issue is in the CLI client's device identity handling:
CLI connects without device identity on loopback: In
auth-profiles-*.js, the functionshouldAttachDeviceIdentityForGatewayCall()returnsfalsewhen:params.tokenis set)127.0.0.1,::1, orlocalhost)Gateway clears scopes when device identity is missing: In
gateway-cli-*.js, the functionhandleMissingDeviceIdentity()callsclearUnboundScopes()when the client doesn't have device identity and isn't Control UI.Result: CLI scopes (
operator.read,operator.write, etc.) are cleared, causing subsequent API calls to fail with "missing scope: operator.read"Steps to Reproduce
{ "gateway": { "port": 18789, "mode": "local", "bind": "loopback", "auth": { "mode": "token", "token": "xxx" } } }openclaw devices list openclaw gateway call device.pair.list --token "xxx" openclaw gateway probeExpected Behavior
CLI commands should work with token auth on loopback without requiring device identity pairing, OR the CLI should always attach device identity when configured.
Current Workaround
None found. The device identity file exists at
~/.openclaw/identity/device.jsonand the device is paired in the gateway, but the CLI doesn't use it for loopback connections with token auth.Suggested Fix
Option 1: Always attach device identity for CLI clients regardless of connection type:
Option 2: Allow CLI clients to skip device identity check when token auth succeeds (similar to
shouldSkipBackendSelfPairingfor BACKEND mode):Environment
Additional Context
The
openclaw gateway probecommand shows:This confirms the gateway connection succeeds but scopes are not properly granted.