screenpipe/screenpipe · error
set sample rate
Error message
set sample rate
What it means
create_mp3_data configures an mp3lame-encoder Builder before building it; set_sample_rate returns Err when the rate is outside LAME's supported range (MP3 supports 8/11.025/12/16/22.05/24/32/44.1/48 kHz) or the builder is otherwise in an invalid state. The code unwraps it with .expect("set sample rate"), so any failure panics the calling thread.
Source
Thrown at crates/screenpipe-audio/src/transcription/openai_compatible/batch.rs:245
// Downsample if needed
let samples: Vec<f32> = if target_sample_rate < sample_rate && sample_rate > 0 {
let ratio = sample_rate / target_sample_rate;
audio_data
.iter()
.enumerate()
.filter(|(i, _)| i % ratio as usize == 0)
.map(|(_, &s)| s)
.collect()
} else {
audio_data.to_vec()
};
let mut encoder = Builder::new().expect("failed to create mp3lame encoder");
encoder.set_num_channels(1).expect("set channels");
encoder
.set_sample_rate(target_sample_rate)
.expect("set sample rate");
encoder
.set_brate(mp3lame_encoder::Bitrate::Kbps64)
.expect("set bitrate");
encoder
.set_quality(mp3lame_encoder::Quality::Good)
.expect("set quality");
let mut encoder = encoder.build().expect("build encoder");
// Convert f32 samples to i16 for mp3lame
let pcm_i16: Vec<i16> = samples
.iter()
.map(|&s| {
let clamped = s.clamp(-1.0, 1.0);
(clamped * i16::MAX as f32) as i16
})
.collect();
let input = MonoPcm(&pcm_i16);View on GitHub (pinned to 4ebf712990)
Solutions
- map the incoming rate to the nearest LAME-supported value instead of only handling >=44100 (e.g. round 22050->24000 or use a whitelist)
- validate sample_rate > 0 and within LAME's supported set before constructing the Builder
- replace .expect with graceful error propagation (Result) so transcription degrades instead of panicking
Example fix
// before
let target_sample_rate = if sample_rate >= 44100 { 16000 } else { sample_rate };
// after
const LAME_RATES: [u32; 9] = [8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000];
let target_sample_rate = if sample_rate >= 44100 { 16000 } else {
*LAME_RATES.iter().min_by_key(|&&r| r.abs_diff(sample_rate)).unwrap_or(&16000)
}; Defensive patterns
Strategy: validation
Validate before calling
const LAME_RATES: [u32; 9] = [8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000];
fn is_lame_supported(rate: u32) -> bool { rate > 0 && LAME_RATES.contains(&rate) }
if !is_lame_supported(sample_rate) { /* resample to nearest supported rate before calling */ } Type guard
fn lame_supported(rate: u32) -> Option<u32> {
const LAME_RATES: [u32; 9] = [8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000];
LAME_RATES.iter().copied().min_by_key(|&r| r.abs_diff(rate))
} Try / catch
match encoder.set_sample_rate(target) {
Ok(b) => b,
Err(e) => { error!("sample rate {} rejected by LAME: {:?}", target, e); return Err(anyhow!("unsupported sample rate")); }
} Prevention
- only feed LAME sample rates from its supported list; resample anything else first
- treat all mp3lame Builder setters as fallible — never .expect them in production paths
- add a unit test that rounds sample rates from real devices through create_mp3_data
When it happens
Trigger: audio input at a sample rate that is not an MPEG-1/2-supported frequency reaches create_mp3_data — e.g. a device producing 44101 Hz, or a corrupted rate of 0 — because the code only downsamples to 16 kHz when rate >= 44100 and passes anything else through verbatim.
Common situations: unusual audio hardware or virtual audio devices reporting non-standard rates; tests generating synthetic PCM at arbitrary rates; changes to the downsampling branch that let odd rates through.
Related errors
- set bitrate
- set quality
- build encoder
- mp3 encode failed
- mlx transcription panic (likely Metal GPU error): {}
AI-assisted analysis of screenpipe/screenpipe@4ebf712990 (2026-09-01).
Data as JSON: /api/errors/17df6bcb3be48316.
Report an issue: GitHub.