zeroclaw-labs/zeroclaw · error · anyhow::Error

Failed to fetch bot info: {}

Error message

Failed to fetch bot info: {}

What it means

fetch_bot_username() GETs the Bot API `getMe` endpoint (during startup/pairing to learn the bot's @username) and bails with the raw HTTP status on any non-2xx. It is usually the first authenticated call against the Bot API, so a failure almost always means the bot_token or api_base is wrong before anything else runs.

Source

Thrown at crates/zeroclaw-channels/src/telegram.rs:1533

    async fn classify_edit_message_response(resp: reqwest::Response) -> EditMessageResult {
        if resp.status().is_success() {
            return EditMessageResult::Success;
        }

        let status = resp.status();
        let body = resp.text().await.unwrap_or_default();
        if body.contains("message is not modified") {
            return EditMessageResult::NotModified;
        }

        EditMessageResult::Failed(status)
    }

    async fn fetch_bot_username(&self) -> anyhow::Result<String> {
        let resp = self.http_client().get(self.api_url("getMe")).send().await?;

        if !resp.status().is_success() {
            anyhow::bail!("Failed to fetch bot info: {}", resp.status());
        }

        let data: serde_json::Value = resp.json().await?;
        let result = data
            .get("result")
            .context("missing result in getMe response")?;
        let username = result
            .get("username")
            .and_then(|u| u.as_str())
            .context("Bot username not found in response")?;

        // Cache the bot's user ID for reply-to-self detection
        if let Some(id) = result.get("id").and_then(|i| i.as_i64()) {
            let mut cache = self.bot_id.lock();
            *cache = Some(id);
        }

        Ok(username.to_string())

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Verify the token directly: `curl https://api.telegram.org/bot<TOKEN>/getMe` must return "ok":true.
  2. If api_base is overridden for a local Bot API server, confirm the server is up and the base URL/path is correct.
  3. Re-issue the token via @BotFather and update it with `zeroclaw config set channels.telegram.<alias>.bot_token <new-token>`.
  4. Restart the channel/process after fixing credentials.
Defensive patterns

Strategy: validation

Validate before calling

// preflight the credentials before wiring the channel
let resp = reqwest::get(format!("{api_base}/bot{token}/getMe")).await?;
anyhow::ensure!(resp.status().is_success(), "bot token rejected: {}", resp.status());

Type guard

fn is_auth_failure(err: &anyhow::Error) -> bool {
    err.to_string().contains("Failed to fetch bot info: 401")
}

Try / catch

if let Err(e) = channel.fetch_bot_username().await {
    if is_auth_failure(&e) {
        anyhow::bail!("bot_token invalid or revoked; reissue with @BotFather and update config");
    }
    return Err(e);
}

Prevention

When it happens

Trigger: 401 Unauthorized with an invalid or revoked bot_token; 404 from a custom api_base pointing at a local telegram-bot-api-server that is not running or has the wrong path; an intermediate proxy returning an error page status.

Common situations: Token regenerated with @BotFather after the old one was revoked; copy-paste typo in the token; api_base override for a self-hosted Bot API server misconfigured; captive portals or corporate proxies intercepting the request.

Related errors


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