{"record":{"id":"fd0bd0902e3a7f10","repo":"Zackriya-Solutions/meetily","slug":"decode-task-panicked","errorCode":null,"errorMessage":"Decode task panicked: {}","messagePattern":"Decode task panicked: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"frontend/src-tauri/src/audio/retranscription.rs","lineNumber":205,"sourceCode":"        \"Starting retranscription for meeting {} with language {:?}, model {:?}, provider {:?}\",\n        meeting_id, language, model, provider\n    );\n\n    // Emit progress: decoding\n    emit_progress(&app, &meeting_id, \"decoding\", 5, \"Decoding audio file...\");\n\n    // Check for cancellation\n    if RETRANSCRIPTION_CANCELLED.load(Ordering::SeqCst) {\n        return Err(anyhow!(\"Retranscription cancelled\"));\n    }\n\n    // Decode the audio file (CPU-intensive, run in blocking task)\n    let path_for_decode = audio_path.clone();\n    let decoded = tokio::task::spawn_blocking(move || {\n        decode_audio_file(&path_for_decode)\n    })\n    .await\n    .map_err(|e| anyhow!(\"Decode task panicked: {}\", e))??;\n    let duration_seconds = decoded.duration_seconds;\n\n    info!(\n        \"Decoded audio: {:.2}s, {}Hz, {} channels\",\n        duration_seconds, decoded.sample_rate, decoded.channels\n    );\n\n    emit_progress(&app, &meeting_id, \"decoding\", 15, \"Converting audio format...\");\n\n    // Check for cancellation\n    if RETRANSCRIPTION_CANCELLED.load(Ordering::SeqCst) {\n        return Err(anyhow!(\"Retranscription cancelled\"));\n    }\n\n    // Convert to 16kHz mono format (CPU-intensive, run in blocking task)\n    let audio_samples = tokio::task::spawn_blocking(move || {\n        decoded.to_whisper_format()\n    })","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/Zackriya-Solutions/meetily/blob/0281737d87d26352fb0adc78c8c0975f691b23d1/frontend/src-tauri/src/audio/retranscription.rs#L187-L223","documentation":"decode_audio_file ran inside tokio::task::spawn_blocking and the task ended in a panic; the JoinError is reported as 'Decode task panicked'. The decoder crashed on its input (an unwrap/index/alloc failure inside the decoding path) rather than returning an Err, so the audio file itself is the prime suspect.","triggerScenarios":"Corrupted or truncated audio file - e.g., the app was killed mid-save leaving a WAV/M4A header claiming more data than the file holds; a container/codec edge case that trips an unwrap in the decoder; allocation failure on a pathologically large file.","commonSituations":"Retranscribing a recording whose save was interrupted by a crash or force-quit; partially copied or synced audio files; exotic files produced by nonstandard recorders.","solutions":["Open the file in another player to verify integrity; re-export or discard damaged audio","Log the panic payload (join_error.into_panic() downcast to String/&str) to locate the decoder crash site","Pre-validate the file (header sanity, size vs. declared length) before decode, or wrap decode_audio_file in catch_unwind to convert panics into errors"],"exampleFix":"// before\nlet decoded = tokio::task::spawn_blocking(move || decode_audio_file(&path))\n    .await\n    .map_err(|e| anyhow!(\"Decode task panicked: {}\", e))??;\n\n// after - convert the panic into an error carrying the payload\nlet decoded = tokio::task::spawn_blocking(move || {\n    std::panic::catch_unwind(|| decode_audio_file(&path))\n        .map_err(|p| anyhow!(\"decode panicked: {:?}\", p.downcast_ref::<String>().cloned()))\n})\n.await\n.map_err(|e| anyhow!(\"Decode task panicked: {}\", e))???;","handlingStrategy":"try-catch","validationCode":"// Cheap pre-decode sanity: header present and size plausible\nfn audio_plausibly_valid(path: &std::path::Path) -> bool {\n    let mut buf = [0u8; 12];\n    match std::fs::File::open(path) {\n        Ok(mut f) => std::io::Read::read_exact(&mut f, &mut buf).is_ok() && buf.len() == 12,\n        Err(_) => false,\n    }\n}","typeGuard":null,"tryCatchPattern":"Treat the JoinError distinctly from decode errors: inspect .is_panic() and downcast into_panic() for the message; report 'audio file is corrupt or unsupported' to the user and offer file repair/replacement rather than retry (retrying a panic on the same input will panic again).","preventionTips":["Write audio files atomically (temp name + rename) so interrupted saves never leave truncated files","Wrap the decoder in catch_unwind to convert panics into Result errors with context","Test retranscription against truncated/corrupt fixtures in CI to catch decoder panics early"],"tags":["panic","audio-decode","spawn-blocking","corrupt-file","join-error"],"backgroundTag":"blocking-task-panic","analyzedSha":"0281737d87d26352fb0adc78c8c0975f691b23d1","analyzedAt":"2026-08-16T20:57:52.567Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}