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

audio download failed: {}

Error message

audio download failed: {}

What it means

The initial download of a Lark audio resource (im/v1/messages/{message_id}/resources) returned non-2xx and did not look like a stale-token response, so no refresh was attempted. Only the status is included. This is the no-refresh twin of error 106 and usually indicates the resource identifier or access is wrong, not the token.

Source

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

                self.invalidate_token().await;
                let token = self.get_tenant_access_token().await?;
                let resp = self
                    .http_client()
                    .get(&url)
                    .header("Authorization", format!("Bearer {token}"))
                    .send()
                    .await?;
                if !resp.status().is_success() {
                    anyhow::bail!(
                        "audio download failed after token refresh: {}",
                        resp.status()
                    );
                }
                let bytes = Self::stream_audio_bytes(resp).await?;
                return Ok((bytes, inferred_audio_filename(file_key)));
            }

            anyhow::bail!("audio download failed: {}", status);
        }
        let bytes = Self::stream_audio_bytes(resp).await?;
        Ok((bytes, inferred_audio_filename(file_key)))
    }

    async fn try_transcribe_audio_message(
        &self,
        message_id: &str,
        content: &str,
        manager: &super::transcription::TranscriptionManager,
    ) -> Option<String> {
        let file_key = serde_json::from_str::<serde_json::Value>(content)
            .ok()
            .and_then(|v| {
                v.get("file_key")
                    .and_then(|k| k.as_str())
                    .map(str::to_owned)
            })?;

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Branch on the reported status: 404 -> skip (expired resource); 403 -> grant and publish the message-resource read scope; 400 -> verify file_key handling; 5xx -> retry later.
  2. Process audio events promptly to stay inside Lark's resource retention window.
  3. If this fires for every audio message, test one file_key manually with a fresh token to isolate scope vs expiry.
Defensive patterns

Strategy: fallback

Type guard

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

Try / catch

if is_lark_audio_download_error(&e) {
    tracing::warn!(error = %e, "lark audio unavailable (expired/forbidden); continuing without it");
    return Ok(());
}

Prevention

When it happens

Trigger: 404 for an expired or already-deleted message resource, 403 for a resource the bot lacks permission to read, 400 for an invalid file_key, or 5xx during Lark incidents.

Common situations: Delayed processing after webhook backlogs, users deleting messages before the bot fetches audio, missing message-resource read scope on the app.

Related errors


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