{"record":{"id":"cc435924b8201d58","repo":"zeroclaw-labs/zeroclaw","slug":"ws-endpoint-failed-code-msg","errorCode":null,"errorMessage":"WS endpoint failed: code={} msg={}","messagePattern":"WS endpoint failed: code=(.+?) msg=(.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/lark.rs","lineNumber":1079,"sourceCode":"        Ok(response)\n    }\n\n    /// POST /callback/ws/endpoint → (wss_url, client_config)\n    async fn get_ws_endpoint(&self) -> anyhow::Result<(String, WsClientConfig)> {\n        let resp = self\n            .http_client()\n            .post(format!(\"{}/callback/ws/endpoint\", self.ws_base()))\n            .header(\"locale\", self.platform.locale_header())\n            .json(&serde_json::json!({\n                \"AppID\": self.app_id,\n                \"AppSecret\": self.app_secret,\n            }))\n            .send()\n            .await?\n            .json::<WsEndpointResp>()\n            .await?;\n        if resp.code != 0 {\n            anyhow::bail!(\n                \"WS endpoint failed: code={} msg={}\",\n                resp.code,\n                resp.msg.as_deref().unwrap_or(\"(none)\")\n            );\n        }\n        let ep = resp.data.ok_or_else(|| {\n            ::zeroclaw_log::record!(\n                ERROR,\n                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                    .with_outcome(::zeroclaw_log::EventOutcome::Failure),\n                \"WS endpoint: empty data\"\n            );\n            anyhow::Error::msg(\"WS endpoint: empty data\")\n        })?;\n        Ok((ep.url, ep.client_config.unwrap_or_default()))\n    }\n\n    /// WS long-connection event loop.  Returns Ok(()) when the connection closes","sourceCodeStart":1061,"sourceCodeEnd":1097,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/lark.rs#L1061-L1097","documentation":"Before the Lark long-connection event loop can open a WebSocket, get_ws_endpoint POSTs the app credentials to {ws_base}/callback/ws/endpoint to provision a wss URL; Lark answers with its code/msg envelope and any non-zero code bails here with the server's msg (or '(none)'). The error text is the passthrough of Lark's provisioning refusal — most commonly bad AppID/AppSecret, or the app not being configured for long-connection (WebSocket) event mode on the open platform.","triggerScenarios":"listen_ws calls get_ws_endpoint; the POST succeeds but resp.code != 0 — wrong app_id/app_secret in config, the Feishu/Lark app has 'Long connection' event subscription disabled in the developer console, or domain/region mismatch (feishu endpoint used with a Larksuite app).","commonSituations":"Credentials rotated or typoed in config.toml; the app was created with webhook event mode and never switched to long-connection mode; using open.larksuite.cn vs open.feishu.cn base for the wrong tenant.","solutions":["Re-copy AppID/AppSecret from the Feishu/Lark developer console into the channel config and confirm no stray whitespace","In the developer console under Events & Callbacks, switch the app to 'Long connection' (WebSocket) mode and republish the app version","Make sure the configured domain matches the app's platform (feishu.cn vs larksuite.com)","The msg field in the error is Lark's own reason — paste it into the Lark error-code docs for the exact fix"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match lark_channel.listen(tx).await {\n    Err(e) if e.to_string().contains(\"WS endpoint failed\") => {\n        // read code/msg: credential or long-connection-mode problem;\n        // surface to operator — retrying without a config/console fix will keep failing\n    }\n    other => { let _ = other?; }\n}","preventionTips":["Enable 'Long connection' event mode for the app in the Feishu/Lark console before deploying the channel","Verify AppID/AppSecret resolve correctly and match the platform domain (feishu vs larksuite)","Use the server's msg field verbatim when debugging — it names the exact provisioning refusal"],"tags":["lark","feishu","websocket","app-credentials","events"],"backgroundTag":"websocket-endpoint-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}