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

bot info failed: code={code}, body={body}

Error message

bot info failed: code={code}, body={body}

What it means

bot/v3/info returned HTTP 200 but a non-zero Lark business code in the JSON body. Lark delivers application-level errors (permission denied, app misconfiguration) inside a 200 response, so this is the 'logical' failure twin of error 103. The code and full body are included in the message, which maps directly to Lark's error-code documentation.

Source

Thrown at crates/zeroclaw-channels/src/lark.rs:1859

            self.invalidate_token().await;
            let refreshed = self.get_tenant_access_token().await?;
            let (retry_status, retry_body) = self.fetch_bot_open_id_with_token(&refreshed).await?;
            if !retry_status.is_success() {
                anyhow::bail!(
                    "bot info request failed after token refresh: status={retry_status}, body={retry_body}"
                );
            }
            retry_body
        } else {
            if !status.is_success() {
                anyhow::bail!("bot info request failed: status={status}, body={body}");
            }
            body
        };

        let code = body.get("code").and_then(|c| c.as_i64()).unwrap_or(-1);
        if code != 0 {
            anyhow::bail!("bot info failed: code={code}, body={body}");
        }

        let bot_open_id = body
            .pointer("/bot/open_id")
            .or_else(|| body.pointer("/data/bot/open_id"))
            .and_then(|v| v.as_str())
            .map(str::trim)
            .filter(|v| !v.is_empty())
            .map(str::to_owned);

        self.set_resolved_bot_open_id(bot_open_id.clone());
        Ok(bot_open_id)
    }

    async fn ensure_bot_open_id(&self) {
        if !self.mention_only || self.resolved_bot_open_id().is_some() {
            return;
        }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Open the Lark console -> app -> Permissions & Scopes, grant the bot info scope, and re-publish a new app version.
  2. Ensure the tenant admin has approved the requested scopes.
  3. Restart the channel after permission changes so a fresh token carrying the new grants is fetched.
  4. Map the code= value in the message against Lark's error-code reference for the precise cause.
Defensive patterns

Strategy: validation

Validate before calling

// Startup permission probe: bot info must return code 0 before the channel goes live
let body = lark.fetch_bot_info_body().await?;
let code = body.get("code").and_then(|c| c.as_i64()).unwrap_or(-1);
anyhow::ensure!(code == 0, "Lark app missing bot-info permission (code={code}); grant scope and republish");

Try / catch

if let Err(e) = lark.fetch_bot_open_id().await {
    if e.to_string().contains("bot info failed: code=") {
        // permission-side failure: retrying with a new token will not help
        return Err(e.context("grant the bot-info scope in the Lark console and republish"));
    }
    return Err(e);
}

Prevention

When it happens

Trigger: The tenant_access_token is valid but the app lacks the permission scope required by GET /bot/v3/info, the token was issued for a different app than the one serving the request, or the bot has been disabled for the tenant.

Common situations: Bot capability added but scopes not re-granted or the app version not re-published; pending tenant admin approval for requested scopes; custom app bot switched off by an administrator after initially working.

Related errors


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