diff --git a/src/gateway/server/ws-connection/message-handler.ts b/src/gateway/server/ws-connection/message-handler.ts index 948d6cefb..5c91c4eb9 100644 --- a/src/gateway/server/ws-connection/message-handler.ts +++ b/src/gateway/server/ws-connection/message-handler.ts @@ -664,12 +664,25 @@ export function attachGatewayWsMessageHandler(params: { requestId: pairing.request.requestId, reason, }); + const helpMessage = + `Device pairing required. ` + + `On the OpenClaw server machine, run: ` + + `'openclaw devices list' to see pending requests, then ` + + `'openclaw devices approve ${pairing.request.requestId}' to approve this device. ` + + `Alternatively, access the Control UI from the server's localhost to approve.`; send({ type: "res", id: frame.id, ok: false, - error: errorShape(ErrorCodes.NOT_PAIRED, "pairing required", { - details: { requestId: pairing.request.requestId }, + error: errorShape(ErrorCodes.NOT_PAIRED, helpMessage, { + details: { + requestId: pairing.request.requestId, + deviceId: device.id, + instructions: { + list: "openclaw devices list", + approve: `openclaw devices approve ${pairing.request.requestId}`, + } + }, }), }); close(1008, "pairing required"); diff --git a/ui/src/ui/gateway.ts b/ui/src/ui/gateway.ts index fc8dde08a..122df9fea 100644 --- a/ui/src/ui/gateway.ts +++ b/ui/src/ui/gateway.ts @@ -219,11 +219,13 @@ export class GatewayBrowserClient { this.backoffMs = 800; this.opts.onHello?.(hello); }) - .catch(() => { + .catch((err) => { if (canFallbackToShared && deviceIdentity) { clearDeviceAuthToken({ deviceId: deviceIdentity.deviceId, role }); } - this.ws?.close(CONNECT_FAILED_CLOSE_CODE, "connect failed"); + // Extract the error message to pass to onClose handler + const errorMessage = err instanceof Error ? err.message : "connect failed"; + this.ws?.close(CONNECT_FAILED_CLOSE_CODE, errorMessage); }); }