zeroclaw-labs/zeroclaw · error · anyhow::Error
WS endpoint failed: code={} msg={}
Error message
WS endpoint failed: code={} msg={} What it means
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.
Source
Thrown at crates/zeroclaw-channels/src/lark.rs:1079
Ok(response)
}
/// POST /callback/ws/endpoint → (wss_url, client_config)
async fn get_ws_endpoint(&self) -> anyhow::Result<(String, WsClientConfig)> {
let resp = self
.http_client()
.post(format!("{}/callback/ws/endpoint", self.ws_base()))
.header("locale", self.platform.locale_header())
.json(&serde_json::json!({
"AppID": self.app_id,
"AppSecret": self.app_secret,
}))
.send()
.await?
.json::<WsEndpointResp>()
.await?;
if resp.code != 0 {
anyhow::bail!(
"WS endpoint failed: code={} msg={}",
resp.code,
resp.msg.as_deref().unwrap_or("(none)")
);
}
let ep = resp.data.ok_or_else(|| {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure),
"WS endpoint: empty data"
);
anyhow::Error::msg("WS endpoint: empty data")
})?;
Ok((ep.url, ep.client_config.unwrap_or_default()))
}
/// WS long-connection event loop. Returns Ok(()) when the connection closesView on GitHub (pinned to 88bb9c8533)
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
Defensive patterns
Strategy: try-catch
Try / catch
match lark_channel.listen(tx).await {
Err(e) if e.to_string().contains("WS endpoint failed") => {
// read code/msg: credential or long-connection-mode problem;
// surface to operator — retrying without a config/console fix will keep failing
}
other => { let _ = other?; }
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- send failed {context}: status={status}, body={body}
- send failed {context}: code={code}, body={body}
- initial ping failed
- tenant_access_token request failed: status={status}, body={d
- tenant_access_token failed: {msg}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/cc435924b8201d58.
Report an issue: GitHub.