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

audio download exceeds {} byte limit

Error message

audio download exceeds {} byte limit

What it means

stream_audio_bytes downloads a Lark audio resource chunk by chunk and aborts as soon as the accumulated bytes exceed MAX_LARK_AUDIO_BYTES, a compile-time constant of 25 MiB (25 * 1024 * 1024, lark.rs:23). The limit protects the process from buffering unbounded downloads in memory, since the whole body is collected into a Vec<u8> for transcription. It is not configurable at runtime.

Source

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

            }
            Err(err) => {
                ::zeroclaw_log::record!(
                    WARN,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
                        .with_outcome(::zeroclaw_log::EventOutcome::Unknown)
                        .with_attrs(::serde_json::json!({"err": err.to_string()})),
                    "failed to resolve bot open_id: ; mention_only group messages will be ignored"
                );
            }
        }
    }

    async fn stream_audio_bytes(mut resp: reqwest::Response) -> anyhow::Result<Vec<u8>> {
        let mut body = Vec::new();
        while let Some(chunk) = resp.chunk().await? {
            body.extend_from_slice(&chunk);
            if body.len() as u64 > MAX_LARK_AUDIO_BYTES {
                anyhow::bail!("audio download exceeds {} byte limit", MAX_LARK_AUDIO_BYTES);
            }
        }
        Ok(body)
    }

    async fn download_audio_resource(
        &self,
        message_id: &str,
        file_key: &str,
    ) -> anyhow::Result<(Vec<u8>, String)> {
        let url = format!(
            "{}/im/v1/messages/{message_id}/resources/{file_key}?type=file",
            self.api_base()
        );
        let token = self.get_tenant_access_token().await?;
        let resp = self
            .http_client()
            .get(&url)

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Treat as expected degradation: skip transcription for oversized audio and, if desired, notify the user.
  2. If the pipeline genuinely needs larger audio, raise the const MAX_LARK_AUDIO_BYTES in crates/zeroclaw-channels/src/lark.rs and rebuild, keeping in mind the full body is held in memory.
  3. Check Content-Length before streaming so oversized resources are rejected before the download starts.
  4. Verify the file_key/content_type mapping is not misrouting large non-audio media into the audio path.

Example fix

// before (crates/zeroclaw-channels/src/lark.rs:23)
const MAX_LARK_AUDIO_BYTES: u64 = 25 * 1024 * 1024;

// after (raises the in-memory cap; whole body is still buffered)
const MAX_LARK_AUDIO_BYTES: u64 = 50 * 1024 * 1024;
Defensive patterns

Strategy: validation

Validate before calling

// Reject oversized downloads before streaming anything
if let Some(len) = resp.content_length() {
    anyhow::ensure!(
        len <= MAX_LARK_AUDIO_BYTES,
        "lark audio content-length {len} exceeds {} cap; skipping download",
        MAX_LARK_AUDIO_BYTES
    );
}

Type guard

fn is_audio_size_error(err: &anyhow::Error) -> bool {
    err.to_string().contains("audio download exceeds")
}

Try / catch

if is_audio_size_error(&e) {
    // expected degradation, not a fault: skip transcription, keep processing the message
    tracing::warn!(error = %e, "oversized lark audio; skipping transcription");
    return Ok(());
}

Prevention

When it happens

Trigger: An inbound voice/audio message whose resource exceeds 25 MiB: very long voice notes, high-bitrate audio files, or a file_key/content_type mapping that routes a large video into the audio download path.

Common situations: Users forwarding long recordings, users sending audio files (m4a/mp3) instead of recorded voice notes, or upstream content-type changes reclassifying large media as audio.

Related errors


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