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

  1. Confirm the token works: `curl -H "Authorization: Bearer <token>" <server>/api/v4/users/me`
  2. Make sure `base_url` and the token belong to the same Mattermost instance
  3. If logging in with login_id/password, verify those credentials in the web client
  4. 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

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


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/33dd593f66a46217. Report an issue: GitHub.