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

non-success status {}

Error message

non-success status {}

What it means

Inside discord_thread_parent, a GET /channels/{channel_id} with the bot token returns non-2xx and bails with this bare status text. The caller wraps the lookup in a timeout and converts any Err — including this one — into a DEBUG log and a cached None, so thread-parent resolution silently degrades and the error never propagates outward. Common statuses: 403 Missing Permissions (bot lacks VIEW_CHANNELS), 404 (deleted channel or malformed id), 401 (bad token).

Source

Thrown at crates/zeroclaw-channels/src/discord/mod.rs:1520

    let url = format!("https://discord.com/api/v10/channels/{channel_id}");
    let lookup = async {
        let resp = client
            .get(&url)
            .header("Authorization", format!("Bot {bot_token}"))
            .send()
            .await
            .map_err(|e| {
                ::zeroclaw_log::record!(
                    ERROR,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                        .with_attrs(::serde_json::json!({"error": format!("{}", e)})),
                    "request failed"
                );
                anyhow::Error::msg(format!("request failed: {e}"))
            })?;
        if !resp.status().is_success() {
            anyhow::bail!("non-success status {}", resp.status());
        }
        let body: serde_json::Value = resp.json().await.map_err(|e| {
            ::zeroclaw_log::record!(
                ERROR,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({"error": format!("{}", e)})),
                "body parse failed"
            );
            anyhow::Error::msg(format!("body parse failed: {e}"))
        })?;
        let is_thread = body
            .get("type")
            .and_then(serde_json::Value::as_u64)
            .map(is_thread_channel_type)
            .unwrap_or(false);
        Ok::<Option<String>, anyhow::Error>(if is_thread {
            body.get("parent_id")

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Grant the bot VIEW_CHANNELS on channels where thread routing matters (channel and role overrides)
  2. Treat as non-fatal — the code already degrades to no thread parent; fixing permissions restores resolution
  3. If 404s dominate, verify channel ids come from gateway events, not user input
  4. If every lookup 401s, the bot token itself is invalid
Defensive patterns

Strategy: fallback

Validate before calling

// Validate the bot token once at startup so 401s are impossible mid-run:
// GET https://discord.com/api/v10/users/@me with `Bot {token}` must return 200.
// The lookup itself already degrades to a cached None on any error.

Prevention

When it happens

Trigger: Bot invited without View Channel on the target channel; channel deleted between message receipt and lookup; a sentinel or malformed id passed as a channel id (guaranteed non-2xx); bot token revoked mid-session.

Common situations: Locked/private channels where the bot receives gateway events but cannot fetch the channel object; bot removed from a guild while its threads still process; permission reorganizations by server admins.

Related errors


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