cjpais/Handy · error
threshold must be between 0.0 and 1.0
Error message
threshold must be between 0.0 and 1.0
What it means
SileroVad::new validates its probability threshold argument before creating the ONNX engine: the value must lie in 0.0..=1.0 inclusive. The bail happens before any resource is loaded, so this is a caller-contract violation, not an environment failure. NaN also fails the range check and triggers the same bail.
Source
Thrown at src-tauri/src/audio_toolkit/vad/silero.rs:21
use vad_rs::Vad;
use super::{VadFrame, VoiceActivityDetector};
use crate::audio_toolkit::constants;
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()
);
}View on GitHub (pinned to 98a4d80cce)
Solutions
- Clamp before constructing: SileroVad::new(path, t.clamp(0.0, 1.0))
- Check the unit of the incoming value — percent vs fraction — and divide by 100.0 at the settings boundary
- Validate and reject out-of-range values in the settings UI/store so they never reach the constructor
- If NaN appears, trace the upstream math (division by zero, empty-window average)
Example fix
// before
let silero = SileroVad::new(vad_path, user_threshold)?;
// after
let threshold = if user_threshold.is_finite() {
user_threshold.clamp(0.0, 1.0)
} else {
VAD_THRESHOLD // fall back to the built-in default
};
let silero = SileroVad::new(vad_path, threshold)?; Defensive patterns
Strategy: validation
Validate before calling
fn normalized_threshold(raw: f32) -> Option<f32> {
if raw.is_finite() && (0.0..=1.0).contains(&raw) {
Some(raw)
} else {
None
}
}
// before constructing:
let t = normalized_threshold(user_value).unwrap_or(VAD_THRESHOLD);
let silero = SileroVad::new(vad_path, t)?; Try / catch
match SileroVad::new(path, t) {
Ok(v) => v,
Err(e) if e.to_string().contains("threshold must be between") => {
// fall back to the default threshold rather than failing the session
SileroVad::new(path, VAD_THRESHOLD)?
}
Err(e) => return Err(e),
} Prevention
- Clamp user-supplied sensitivity at the settings boundary, not at the call site
- Keep percentages (0-100) and fractions (0.0-1.0) as separate types/fields to avoid unit confusion
- Reject NaN/Infinity explicitly — range checks alone catch them, but the log should say why
When it happens
Trigger: Calling SileroVad::new(model_path, threshold) with threshold < 0.0 or > 1.0 (e.g. 1.5), or with NaN/Infinity (a divide-by-zero or bad parse feeding the value). In this repo the caller passes the compile-time VAD_THRESHOLD constant, so hitting it means custom code or a modified constant.
Common situations: Exposing VAD sensitivity as a user setting without clamping; parsing a percentage (0-100) from config and passing it where a 0.0-1.0 fraction is expected; computing a threshold dynamically (average, ratio) that can go out of range or NaN.
Related errors
- Failed to create VAD: {e}
- Failed to create SileroVad: {}
- No compute device with index {index} (see --list-devices)
- Failed to create AudioRecorder: {}
- Failed to resolve VAD path: {}
AI-assisted analysis of cjpais/Handy@98a4d80cce (2026-08-16).
Data as JSON: /api/errors/84408a9e8ea9903c.
Report an issue: GitHub.