{"record":{"id":"3f116ad91a94d89d","repo":"cjpais/Handy","slug":"failed-to-create-resampler","errorCode":null,"errorMessage":"Failed to create resampler","messagePattern":"Failed to create resampler","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src-tauri/src/audio_toolkit/audio/resampler.rs","lineNumber":31,"sourceCode":"    in_hz: usize,\n    out_hz: usize,\n    /// Samples in/out of the inner resampler; `finish()` uses the pair to\n    /// know how much real audio (~10-30ms) its delay line still holds.\n    in_count: usize,\n    out_count: usize,\n}\n\nimpl FrameResampler {\n    pub fn new(in_hz: usize, out_hz: usize, frame_dur: Duration) -> Self {\n        let frame_samples = ((out_hz as f64 * frame_dur.as_secs_f64()).round()) as usize;\n        assert!(frame_samples > 0, \"frame duration too short\");\n\n        // Use fixed chunk size instead of GCD-based\n        let chunk_in = RESAMPLER_CHUNK_SIZE;\n\n        let resampler = (in_hz != out_hz).then(|| {\n            FftFixedIn::<f32>::new(in_hz, out_hz, chunk_in, 1, 1)\n                .expect(\"Failed to create resampler\")\n        });\n\n        Self {\n            resampler,\n            chunk_in,\n            in_buf: Vec::with_capacity(chunk_in),\n            frame_samples,\n            pending: Vec::with_capacity(frame_samples),\n            in_hz,\n            out_hz,\n            in_count: 0,\n            out_count: 0,\n        }\n    }\n\n    pub fn push(&mut self, mut src: &[f32], mut emit: impl FnMut(&[f32])) {\n        if self.resampler.is_none() {\n            self.emit_frames(src, &mut emit);","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/cjpais/Handy/blob/c6fa60da2f13a5af660fba17f37af548855119c5/src-tauri/src/audio_toolkit/audio/resampler.rs#L13-L49","documentation":"A panic (expect) inside FrameResampler::new when rubato's FftFixedIn::new returns Err for the given in_hz/out_hz/chunk_in combination. FftFixedIn validates its arguments — zero sample rates or a fixed chunk size that cannot map to a valid FFT length for the in-to-out ratio produce an Err, which this code converts into a process abort. Because it is an expect, there is no Result to handle.","triggerScenarios":"Constructing FrameResampler with in_hz != out_hz while RESAMPLER_CHUNK_SIZE is not a usable chunk for that ratio, or with a garbage/zero sample rate coming from the device config (e.g. a cached config or device query that returned 0 Hz).","commonSituations":"A device reporting an unusual native rate (8000/11025 Hz) that the fixed chunk cannot express for the 16000 Hz target; corrupted or zero sample_rate from the config cache after a device changed state; changing RESAMPLER_CHUNK_SIZE or the output rate without validating FFT compatibility for all real devices.","solutions":["Log in_hz/out_hz at construction and treat 0 or absurd rates from the device config as a config-fetch failure (the code already drops a stale cache on open failure, so retry re-queries)","Pick a chunk_in compatible with the actual ratios; add a unit test constructing the resampler for every device rate you support at the target rate","Keep the in_hz == out_hz passthrough path so only genuine resamples reach FftFixedIn","Replace .expect with fallible construction (map_err into an io::Error) so callers can fail gracefully instead of aborting"],"exampleFix":"// before\nlet resampler = (in_hz != out_hz).then(|| {\n    FftFixedIn::<f32>::new(in_hz, out_hz, chunk_in, 1, 1)\n        .expect(\"Failed to create resampler\")\n});\n\n// after — construction becomes fallible instead of aborting the process\nlet resampler = (in_hz != out_hz)\n    .then(|| FftFixedIn::<f32>::new(in_hz, out_hz, chunk_in, 1, 1))\n    .transpose()\n    .map_err(|e| {\n        std::io::Error::new(\n            std::io::ErrorKind::InvalidInput,\n            format!(\"resampler {in_hz}->{out_hz} chunk {chunk_in}: {e}\"),\n        )\n    })?;","handlingStrategy":"validation","validationCode":"// Validate rates and chunk compatibility before constructing the resampler\nif in_hz == 0 || out_hz == 0 {\n    return Err(std::io::Error::new(\n        std::io::ErrorKind::InvalidInput,\n        format!(\"invalid sample rate {in_hz}->{out_hz}\"),\n    ));\n}\nif in_hz != out_hz {\n    rubato::FftFixedIn::<f32>::new(in_hz, out_hz, RESAMPLER_CHUNK_SIZE, 1, 1)\n        .map_err(|e| format!(\"chunk {RESAMPLER_CHUNK_SIZE} invalid for {in_hz}->{out_hz}: {e}\"))?;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Unit-test resampler construction for every (device rate, target rate) pair your fleet reports so CI catches an incompatible chunk before release","Never trust a device-reported sample rate without checking it is > 0","Keep resampler construction fallible in API design (return Result) so callers can skip resampling or fail gracefully"],"tags":["rust","rubato","audio","resampling","panic"],"backgroundTag":"audio-resampler-init-failed","analyzedSha":"c6fa60da2f13a5af660fba17f37af548855119c5","analyzedAt":"2026-08-17T10:29:55.597Z","contentChangedAt":"2026-08-17T10:29:55.597Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}