cjpais/Handy · critical
Failed to create resampler
Error message
Failed to create resampler
What it means
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.
Source
Thrown at src-tauri/src/audio_toolkit/audio/resampler.rs:31
in_hz: usize,
out_hz: usize,
/// Samples in/out of the inner resampler; `finish()` uses the pair to
/// know how much real audio (~10-30ms) its delay line still holds.
in_count: usize,
out_count: usize,
}
impl FrameResampler {
pub fn new(in_hz: usize, out_hz: usize, frame_dur: Duration) -> Self {
let frame_samples = ((out_hz as f64 * frame_dur.as_secs_f64()).round()) as usize;
assert!(frame_samples > 0, "frame duration too short");
// Use fixed chunk size instead of GCD-based
let chunk_in = RESAMPLER_CHUNK_SIZE;
let resampler = (in_hz != out_hz).then(|| {
FftFixedIn::<f32>::new(in_hz, out_hz, chunk_in, 1, 1)
.expect("Failed to create resampler")
});
Self {
resampler,
chunk_in,
in_buf: Vec::with_capacity(chunk_in),
frame_samples,
pending: Vec::with_capacity(frame_samples),
in_hz,
out_hz,
in_count: 0,
out_count: 0,
}
}
pub fn push(&mut self, mut src: &[f32], mut emit: impl FnMut(&[f32])) {
if self.resampler.is_none() {
self.emit_frames(src, &mut emit);View on GitHub (pinned to c6fa60da2f)
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
Example fix
// before
let resampler = (in_hz != out_hz).then(|| {
FftFixedIn::<f32>::new(in_hz, out_hz, chunk_in, 1, 1)
.expect("Failed to create resampler")
});
// after — construction becomes fallible instead of aborting the process
let resampler = (in_hz != out_hz)
.then(|| FftFixedIn::<f32>::new(in_hz, out_hz, chunk_in, 1, 1))
.transpose()
.map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("resampler {in_hz}->{out_hz} chunk {chunk_in}: {e}"),
)
})?; Defensive patterns
Strategy: validation
Validate before calling
// Validate rates and chunk compatibility before constructing the resampler
if in_hz == 0 || out_hz == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("invalid sample rate {in_hz}->{out_hz}"),
));
}
if in_hz != out_hz {
rubato::FftFixedIn::<f32>::new(in_hz, out_hz, RESAMPLER_CHUNK_SIZE, 1, 1)
.map_err(|e| format!("chunk {RESAMPLER_CHUNK_SIZE} invalid for {in_hz}->{out_hz}: {e}"))?;
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Failed to initialize recording manager
- English translations must exist
- No input device found
- {error_message}
- Failed to create AudioRecorder: {}
AI-assisted analysis of cjpais/Handy@c6fa60da2f (2026-08-17).
Data as JSON: /api/errors/3f116ad91a94d89d.
Report an issue: GitHub.