{"record":{"id":"583d363e5316a1c1","repo":"zeroclaw-labs/zeroclaw","slug":"audio-download-failed-status-for-message-mess","errorCode":null,"errorMessage":"audio download failed ({status}) for message {message_id}","messagePattern":"audio download failed \\((.+?)\\) for message (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/zeroclaw-channels/src/line.rs","lineNumber":200,"sourceCode":"/// Download audio/voice message binary from the LINE Content API.\n/// LINE stores message content at `https://api-data.line.me/v2/bot/message/{id}/content`.\n/// Audio messages are typically M4A (`audio/x-m4a`).\nasync fn download_audio_content(\n    client: &reqwest::Client,\n    content_api_base_url: &str,\n    channel_access_token: &str,\n    message_id: &str,\n) -> anyhow::Result<Vec<u8>> {\n    let url = format!(\"{content_api_base_url}/v2/bot/message/{message_id}/content\");\n    let resp = client\n        .get(&url)\n        .bearer_auth(channel_access_token)\n        .send()\n        .await?;\n\n    if !resp.status().is_success() {\n        let status = resp.status();\n        anyhow::bail!(\"audio download failed ({status}) for message {message_id}\");\n    }\n\n    let mut bytes = Vec::new();\n    let mut stream = resp;\n    while let Some(chunk) = stream.chunk().await? {\n        bytes.extend_from_slice(&chunk);\n        if bytes.len() as u64 > MAX_LINE_AUDIO_BYTES {\n            anyhow::bail!(\n                \"audio exceeds {} byte limit for message {message_id}\",\n                MAX_LINE_AUDIO_BYTES\n            );\n        }\n    }\n    Ok(bytes)\n}\n\nfn build_webhook_router(state: Arc<LineState>) -> axum::Router {\n    use axum::{Router, routing::post};","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/line.rs#L182-L218","documentation":"LINE channel: download_audio_content GETs the message content (Messaging API content endpoint) using the channel access token and the message_id, and the response was non-2xx. The message includes the HTTP status and the specific message_id. Transport errors surface separately as reqwest::Error.","triggerScenarios":"401 with a revoked or rotated channel access token, 404 when the message content has passed LINE's retention window or the message_id is invalid, 429/5xx under load or incidents.","commonSituations":"Webhook backlogs processed late so audio content expired; the channel access token was re-issued in the LINE console (old tokens are revoked immediately); wrong token configured for the alias.","solutions":["Branch on status: 401 -> re-copy the current long-term channel access token into [channels.line.<alias>]; 404 -> content expired, skip transcription; 429/5xx -> retry with backoff.","Reduce webhook-to-processing latency so audio is downloaded within the content retention window.","Confirm one token per channel and that no other process uses a stale token for the same channel."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Cheap credential probe before downloading content\nasync fn line_token_ok(client: &reqwest::Client, token: &str) -> bool {\n    client.get(\"https://api.line.me/v2/bot/info\")\n        .bearer_auth(token).send().await\n        .map(|r| r.status().is_success()).unwrap_or(false)\n}","typeGuard":"fn is_line_audio_download_error(err: &anyhow::Error) -> bool {\n    err.to_string().contains(\"audio download failed\")\n}","tryCatchPattern":"match line.download_audio_content(msg_id).await {\n    Ok(bytes) => transcribe(bytes),\n    Err(e) if is_line_audio_download_error(&e) => continue_without_transcription(msg).await,\n    Err(e) => return Err(e),\n}","preventionTips":["Process LINE audio webhooks immediately; content URLs expire.","After re-issuing a channel access token, update config and restart every consumer.","Keep transcription optional in the pipeline."],"tags":["line","audio","download","http","content-expired"],"backgroundTag":"http-error-response","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}