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

Discord send message with files failed ({status}): {err}

Error message

Discord send message with files failed ({status}): {err}

What it means

The multipart variant of the send wrapper posts a message envelope plus file attachments; a non-2xx Discord answer makes it bail with status and body inline. File sends fail differently from plain sends: size limits and attachment counts are the usual offenders.

Source

Thrown at crates/zeroclaw-channels/src/discord/rest.rs:144

            format!("files[{idx}]"),
            Part::bytes(bytes).file_name(filename),
        );
    }

    let resp = client
        .post(&url)
        .header("Authorization", format!("Bot {bot_token}"))
        .multipart(form)
        .send()
        .await?;

    if !resp.status().is_success() {
        let status = resp.status();
        let err = resp
            .text()
            .await
            .unwrap_or_else(|e| format!("<failed to read response body: {e}>"));
        anyhow::bail!("Discord send message with files failed ({status}): {err}");
    }

    extract_message_id(resp).await
}

async fn extract_message_id(resp: reqwest::Response) -> anyhow::Result<String> {
    let body: serde_json::Value = resp.json().await?;
    body.get("id")
        .and_then(|v| v.as_str())
        .map(|s| s.to_string())
        .ok_or_else(|| {
            ::zeroclaw_log::record!(
                WARN,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({"field": "id"})),
                "discord: send response missing id field"
            );

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Check the status/body: size-related 400/413 → compress, truncate, or link the artifact instead of attaching
  2. Cap attachments at 10 per message and split larger batches
  3. Grant the bot Attach Files on the target channel
  4. 401 → fix the bot token
Defensive patterns

Strategy: validation

Validate before calling

// Rust — pre-validate attachments against Discord's limits
const MAX_ATTACHMENTS: usize = 10;
fn attachments_precheck(files: &[FilePayload], cap_bytes: u64) -> Result<(), String> {
    if files.len() > MAX_ATTACHMENTS {
        return Err(format!("{} attachments exceeds Discord's limit of {MAX_ATTACHMENTS}", files.len()));
    }
    for f in files {
        if f.bytes.is_empty() {
            return Err(format!("attachment '{}' is empty", f.name));
        }
        if f.bytes.len() as u64 > cap_bytes {
            return Err(format!("attachment '{}' exceeds the upload cap; compress or link it", f.name));
        }
    }
    Ok(())
}

Prevention

When it happens

Trigger: POST with multipart returns non-success: 413/400 for a file over the bot's upload cap, 400 for more than 10 attachments or a zero-byte file, 403 missing Attach Files permission, 401 bad token.

Common situations: Agent replies attaching generated artifacts (logs, screenshots, builds) larger than the current upload cap; too many files batched into one reply; Attach Files missing on a locked-down channel.

Related errors


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