Zackriya-Solutions/meetily · error · anyhow::Error
VAD task panicked: {}
Error message
VAD task panicked: {} What it means
The .await on tokio::task::spawn_blocking returned a JoinError, which means the thread that runs get_speech_chunks_with_progress panicked, or the Tokio runtime shut down and aborted the blocking task before it finished. The '{}' holds the panic payload text. This is not the VAD error itself: a VAD failure returns normally and produces 'VAD processing failed' at the second map_err (retranscription.rs:263).
Source
Thrown at frontend/src-tauri/src/audio/retranscription.rs:262
VAD_REDEMPTION_TIME_MS,
|vad_progress, segments_found| {
// Map VAD progress (0-100) to overall progress (20-25)
let overall_progress = 20 + (vad_progress as f32 * 0.05) as u32;
emit_progress(
&app_for_vad,
&meeting_id_for_vad,
"vad",
overall_progress,
&format!("Detecting speech segments... {}% ({} found)", vad_progress, segments_found),
);
// Return false to cancel if cancellation requested
!RETRANSCRIPTION_CANCELLED.load(Ordering::SeqCst)
},
)
})
.await
.map_err(|e| anyhow!("VAD task panicked: {}", e))?
.map_err(|e| anyhow!("VAD processing failed: {}", e))?;
let total_segments = speech_segments.len();
info!("VAD detected {} speech segments (redemption_time={}ms)", total_segments, VAD_REDEMPTION_TIME_MS);
// Diagnostic: log segment duration distribution
if !speech_segments.is_empty() {
let durations_ms: Vec<f64> = speech_segments.iter()
.map(|s| s.end_timestamp_ms - s.start_timestamp_ms)
.collect();
let total_speech_ms: f64 = durations_ms.iter().sum();
let avg_duration = total_speech_ms / durations_ms.len() as f64;
let min_duration = durations_ms.iter().cloned().fold(f64::INFINITY, f64::min);
let max_duration = durations_ms.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
info!(
"VAD segment stats: avg={:.0}ms, min={:.0}ms, max={:.0}ms, total_speech={:.1}s/{:.1}s ({:.0}%)",
avg_duration, min_duration, max_duration,
total_speech_ms / 1000.0, duration_seconds,View on GitHub (pinned to 0281737d87)
Solutions
- Re-run with RUST_LOG=debug and read the panic message in '{}' plus the stderr backtrace to find the exact panicking line in audio/vad.rs.
- If the panic occurs on app exit, call cancel_retranscription and wait until is_retranscription_in_progress() returns false before shutting the runtime down.
- Sanitize decoded samples before VAD: replace NaN/inf f32 values with 0.0 right after decoded.to_whisper_format().
- Fix the panicking invariant in the VAD processor (or wrap its body in catch_unwind and convert the panic into a Result error).
Example fix
// before
let audio_samples = tokio::task::spawn_blocking(move || {
decoded.to_whisper_format()
}).await?;
// after: reject non-finite samples before they reach VAD
let audio_samples = tokio::task::spawn_blocking(move || {
let mut s = decoded.to_whisper_format()?;
if s.iter().any(|x| !x.is_finite()) {
s.iter_mut().for_each(|x| {
if !x.is_finite() { *x = 0.0; }
});
}
Ok(s)
}).await??; Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: reject non-finite samples before handing audio to VAD
fn samples_are_finite(samples: &[f32]) -> bool {
samples.iter().all(|x| x.is_finite())
}
// call before spawn_blocking:
// assert_or_bail!(samples_are_finite(&audio_samples), "audio contains non-finite samples"); Try / catch
// Distinguish JoinError (panic/abort) from the VAD Result
match tokio::task::spawn_blocking(move || get_speech_chunks_with_progress(...)).await {
Ok(Ok(segments)) => { /* continue */ }
Ok(Err(vad_err)) => log::warn!("VAD failed: {vad_err}"),
Err(join_err) if join_err.is_panic() => log::error!("VAD panicked: {join_err}"),
Err(_) => log::error!("VAD task aborted (runtime shutdown)"),
} Prevention
- Never exit the app while retranscription is in progress; cancel and await is_retranscription_in_progress() == false first.
- Sanitize decoded audio (drop NaN/inf samples) before any DSP stage.
- Run large-file VAD under RUST_LOG=debug during development to catch panicking edge cases early.
When it happens
Trigger: Calling start_retranscription on audio whose decoded samples trigger a panic inside ContinuousVadProcessor::process_audio (for example non-finite f32 samples or an unexpected chunk shape), a bug in the progress callback, or exiting the app while VAD is still scanning a 35+ minute file so the runtime drops the task.
Common situations: User quits the app during the 'Detecting speech segments' stage; corrupted or mis-decoded audio containing NaN/inf samples; a VAD refactor that breaks an invariant only on edge-case audio lengths.
Related errors
- Decode task join error: {}
- Resample task join error: {}
- VAD task panicked: {}
- Copy task join error: {}
- Decode task panicked: {}
AI-assisted analysis of Zackriya-Solutions/meetily@0281737d87 (2026-08-16).
Data as JSON: /api/errors/083db86ecd5a19b7.
Report an issue: GitHub.