cjpais/Handy · critical · anyhow::Error
Failed to create VAD
Error message
Failed to create VAD: {e} What it means
Wraps a failure of vad_rs::Vad::new() to load the Silero VAD ONNX model from the given path at 16 kHz (constants::WHISPER_SAMPLE_RATE). It fails when the model file does not exist, is not valid ONNX, or the ONNX runtime cannot create a session for it. The threshold is validated before this call, so this error is purely about the model file itself.
Solutions
- Download the Silero model as documented: mkdir -p src-tauri/resources/models && curl -o src-tauri/resources/models/silero_vad_v4.onnx https://blob.handy.computer/silero_vad_v4.onnx
- Verify the file is a complete ONNX binary (size > 1 MB, not an HTML error page from a failed curl).
- Confirm the path passed to SileroVad::new resolves at runtime (resource dir in dev vs. bundled prod) and that tauri.conf.json includes it as a resource.
- Re-download if the file is corrupt; a bad ONNX will fail deterministically.
Example fix
// before
let vad = SileroVad::new(model_path, threshold)?;
// after — fail with an actionable message before hitting the ONNX loader
if !model_path.as_ref().exists() {
anyhow::bail!(
"Silero VAD model missing at {} — run the documented setup download",
model_path.as_ref().display()
);
}
let vad = SileroVad::new(model_path, threshold)?; Defensive patterns
Strategy: validation
Validate before calling
// Before constructing the VAD, assert the model is present and non-trivial
let p = resource_dir.join("models/silero_vad_v4.onnx");
let meta = std::fs::metadata(&p)?;
if meta.len() < 1_000_000 {
anyhow::bail!("Silero model at {} looks truncated ({} bytes)", p.display(), meta.len());
}
let vad = SileroVad::new(&p, threshold)?; Try / catch
match SileroVad::new(&path, threshold) {
Ok(v) => v,
Err(e) => {
error!("VAD init failed ({e}); run the setup download before starting the app");
std::process::exit(1); // startup-critical resource missing
}
} Prevention
- Fetch the Silero ONNX in the build/CI script so a fresh clone always has it.
- Add the model to tauri.conf.json resources so production bundles ship it.
- Fail fast at startup with a clear message instead of first-use.
- Sanity-check file size (>1 MB) to catch HTML error pages saved as .onnx.
When it happens
Trigger: Constructing SileroVad::new(model_path, threshold) when silero_vad_v4.onnx is missing from src-tauri/resources/models/ (dev setup step not run), the file is truncated/corrupt (partial copy), or the path points at a non-ONNX file.
Common situations: Fresh clone without running the 'curl -o src-tauri/resources/models/silero_vad_v4.onnx' setup command; CI builds where resources are not fetched; antivirus/quarantine modifying the .onnx file; bundling the app without including the resource.
Related errors
- Failed to create SileroVad
- Silero VAD error
- Canary transcription failed
- Cohere transcription failed
- error while building tauri application
AI-assisted analysis of cjpais/Handy@c6fa60da2f (2026-08-17).
Data as JSON: /api/errors/601ac44cd569c1b9.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/audio_toolkit/vad/silero.rs:26
const SILERO_FRAME_MS: u32 = 30;
const SILERO_FRAME_SAMPLES: usize =
(constants::WHISPER_SAMPLE_RATE * SILERO_FRAME_MS / 1000) as usize;
pub struct SileroVad {
engine: Vad,
threshold: f32,
}
impl SileroVad {
pub fn new<P: AsRef<Path>>(model_path: P, threshold: f32) -> Result<Self> {
if !(0.0..=1.0).contains(&threshold) {
anyhow::bail!("threshold must be between 0.0 and 1.0");
}
Ok(Self {
engine: Vad::new(&model_path, constants::WHISPER_SAMPLE_RATE as usize)
.map_err(|e| anyhow::anyhow!("Failed to create VAD: {e}"))?,
threshold,
})
}
}
impl VoiceActivityDetector for SileroVad {
fn push_frame<'a>(&'a mut self, frame: &'a [f32]) -> Result<VadFrame<'a>> {
if frame.len() != SILERO_FRAME_SAMPLES {
anyhow::bail!(
"expected {SILERO_FRAME_SAMPLES} samples, got {}",
frame.len()
);
}
let result = self
.engine
.compute(frame)
.map_err(|e| anyhow::anyhow!("Silero VAD error: {e}"))?;View on GitHub (pinned to c6fa60da2f)