{"record":{"id":"84408a9e8ea9903c","repo":"cjpais/Handy","slug":"threshold-must-be-between-0-0-and-1-0","errorCode":null,"errorMessage":"threshold must be between 0.0 and 1.0","messagePattern":"threshold must be between 0\\.0 and 1\\.0","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/src/audio_toolkit/vad/silero.rs","lineNumber":21,"sourceCode":"\nuse vad_rs::Vad;\n\nuse super::{VadFrame, VoiceActivityDetector};\nuse crate::audio_toolkit::constants;\n\nconst SILERO_FRAME_MS: u32 = 30;\nconst SILERO_FRAME_SAMPLES: usize =\n    (constants::WHISPER_SAMPLE_RATE * SILERO_FRAME_MS / 1000) as usize;\n\npub struct SileroVad {\n    engine: Vad,\n    threshold: f32,\n}\n\nimpl SileroVad {\n    pub fn new<P: AsRef<Path>>(model_path: P, threshold: f32) -> Result<Self> {\n        if !(0.0..=1.0).contains(&threshold) {\n            anyhow::bail!(\"threshold must be between 0.0 and 1.0\");\n        }\n\n        Ok(Self {\n            engine: Vad::new(&model_path, constants::WHISPER_SAMPLE_RATE as usize)\n                .map_err(|e| anyhow::anyhow!(\"Failed to create VAD: {e}\"))?,\n            threshold,\n        })\n    }\n}\n\nimpl VoiceActivityDetector for SileroVad {\n    fn push_frame<'a>(&'a mut self, frame: &'a [f32]) -> Result<VadFrame<'a>> {\n        if frame.len() != SILERO_FRAME_SAMPLES {\n            anyhow::bail!(\n                \"expected {SILERO_FRAME_SAMPLES} samples, got {}\",\n                frame.len()\n            );\n        }","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/cjpais/Handy/blob/98a4d80cce8ad41efec2a419b59d9e81229a35d7/src-tauri/src/audio_toolkit/vad/silero.rs#L3-L39","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nlet silero = SileroVad::new(vad_path, user_threshold)?;\n\n// after\nlet threshold = if user_threshold.is_finite() {\n    user_threshold.clamp(0.0, 1.0)\n} else {\n    VAD_THRESHOLD // fall back to the built-in default\n};\nlet silero = SileroVad::new(vad_path, threshold)?;","handlingStrategy":"validation","validationCode":"fn normalized_threshold(raw: f32) -> Option<f32> {\n    if raw.is_finite() && (0.0..=1.0).contains(&raw) {\n        Some(raw)\n    } else {\n        None\n    }\n}\n\n// before constructing:\nlet t = normalized_threshold(user_value).unwrap_or(VAD_THRESHOLD);\nlet silero = SileroVad::new(vad_path, t)?;","typeGuard":null,"tryCatchPattern":"match SileroVad::new(path, t) {\n    Ok(v) => v,\n    Err(e) if e.to_string().contains(\"threshold must be between\") => {\n        // fall back to the default threshold rather than failing the session\n        SileroVad::new(path, VAD_THRESHOLD)?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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"],"tags":["rust","vad","validation","argument-out-of-range"],"backgroundTag":"argument-out-of-range","analyzedSha":"98a4d80cce8ad41efec2a419b59d9e81229a35d7","analyzedAt":"2026-08-16T20:58:09.966Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}