zeroclaw-labs/zeroclaw · warning · anyhow::Error
audio download failed after token refresh: {}
Error message
audio download failed after token refresh: {} What it means
While downloading a Lark audio resource, the first attempt signaled an auth problem (401 or code 99991663); the channel invalidated its token, fetched a fresh one, and retried — and the retried download still returned non-2xx. Since the fresh token did not help, the resource itself is usually the problem (expired, deleted, or access-restricted) rather than authentication. Only the retry status is reported.
Source
Thrown at crates/zeroclaw-channels/src/lark.rs:1952
.await?;
let status = resp.status();
if !status.is_success() {
let body_text = resp.text().await.unwrap_or_default();
let body: serde_json::Value =
serde_json::from_str(&body_text).unwrap_or_else(|_| serde_json::json!({}));
if should_refresh_lark_tenant_token(status, &body) {
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,View on GitHub (pinned to 88bb9c8533)
Solutions
- Reduce processing latency so audio is fetched promptly after the webhook event.
- Branch on status: 404 -> resource expired, skip gracefully; 403 -> check message-resource read permission on the app.
- Confirm the app has the message-resource (im resource) read scope granted and published.
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
match lark.transcribe_audio(msg).await {
Ok(text) => use_text(text),
Err(e) if is_lark_audio_download_error(&e) => {
// expired/deleted resource with a fresh token: reply without transcription
continue_without_transcription(msg).await;
}
Err(e) => return Err(e),
} Prevention
- Process audio-bearing webhooks first; resource retention is finite.
- Make transcription a best-effort stage with its own error boundary.
- Grant and publish the message-resource read scope up front.
When it happens
Trigger: Lark message resources that have expired out of retention, a message deleted by the user before processing, a file with restricted access the bot cannot read, or 404 on an invalid file_key.
Common situations: Queue backlogs mean audio messages are processed long after arrival, past resource retention; users delete messages immediately after sending; retention policy on the tenant shortens resource availability.
Related errors
- audio download failed: {}
- bot info request failed after token refresh: status={retry_s
- audio download exceeds {} byte limit
- send failed after token refresh: status={retry_status}, body
- upload image failed after token refresh: status={retry_statu
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/dce07e170df4041a.
Report an issue: GitHub.