zeroclaw-labs/zeroclaw · error · anyhow::Error
GET /users/me/channels returned {}
Error message
GET /users/me/channels returned {} What it means
When no explicit `channel_ids` are configured (empty list or only `*` wildcards), the channel discovers targets via `GET /api/v4/users/me/channels`. This error fires when that endpoint answers non-2xx — typically 401 for an invalid/expired token, 403 for permission problems, or 5xx during server trouble. Transport failures raise a different, `context`-wrapped error.
Source
Thrown at crates/zeroclaw-channels/src/mattermost.rs:237
.await
.with_context(|| format!("decode /channels/{id} body"))?;
let ty = body.get("type").and_then(|v| v.as_str()).unwrap_or("");
out.push(TargetChannel {
id,
is_direct: is_direct_channel(ty),
});
}
return Ok(out);
}
let resp = self
.http_client()
.get(format!("{}/api/v4/users/me/channels", self.base_url))
.bearer_auth(&token)
.send()
.await
.context("GET /users/me/channels failed")?;
if !resp.status().is_success() {
bail!("GET /users/me/channels returned {}", resp.status());
}
let body: serde_json::Value = resp
.json()
.await
.context("decode /users/me/channels body")?;
let arr = body.as_array().cloned().unwrap_or_default();
Ok(filter_discovered_channels(
&arr,
&self.team_ids,
self.discover_dms,
))
}
/// Return the alias under `[channels.mattermost.<alias>]` that this
/// channel handle is bound to.
pub fn alias(&self) -> &str {
&self.alias
}View on GitHub (pinned to 88bb9c8533)
Solutions
- Confirm the token works: `curl -H "Authorization: Bearer <token>" <server>/api/v4/users/me`
- Make sure `base_url` and the token belong to the same Mattermost instance
- If logging in with login_id/password, verify those credentials in the web client
- Retry startup after transient 5xx; the discovery call runs on every listen
Defensive patterns
Strategy: try-catch
Validate before calling
let resp = client
.get(format!("{base_url}/api/v4/users/me"))
.bearer_auth(&token)
.send()
.await?;
if !resp.status().is_success() {
eprintln!("token invalid: {}", resp.status());
} Try / catch
if let Err(e) = mm_channel.listen(tx).await {
let msg = e.to_string();
if msg.contains("GET /users/me/channels returned 401") {
// rotate token, do not blind-retry
}
} Prevention
- Smoke-test the token against /api/v4/users/me during deployment checks
- Use a long-lived personal access token rather than session tokens that expire
- Monitor first-listen failures distinctly from mid-session failures
When it happens
Trigger: `channel_ids` unset or wildcard-only and the discovery GET returns non-success during `listen_polling`, `listen_websocket`, or `list_target_channels`.
Common situations: Expired or revoked session/personal token; token issued by a different Mattermost server than `base_url`; bot account deactivated; transient 5xx at startup.
Related errors
- login failed ({status}): {body}
- GET /channels/{id} returned {}: explicit channel_id is not a
- Mattermost WebSocket authentication was rejected
- post failed ({status}): {body}
- Mochat send message failed ({status}): {err}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/33dd593f66a46217.
Report an issue: GitHub.