zed-industries/zed · error
Only locked after stream ends
Error message
Only locked after stream ends
What it means
Asserts in the cpal audio capture callback that the samples Mutex is uncontended: samples.try_lock() must succeed because the only other locker (write_out) runs strictly after the stream has ended. If it fires, that ordering was violated — e.g., write_out was called while the input stream was still delivering callbacks, or the stream was not fully stopped before draining.
Source
Thrown at crates/livekit_client/src/record.rs:70
}
fn start_capture(
device: cpal::Device,
config: cpal::SupportedStreamConfig,
samples: Arc<Mutex<Vec<i16>>>,
) -> Result<cpal::Stream> {
let stream = device
.build_input_stream_raw(
&config.config(),
config.sample_format(),
move |data, _: &_| {
let data = crate::get_sample_data(config.sample_format(), data).log_err();
let Some(data) = data else {
return;
};
samples
.try_lock()
.expect("Only locked after stream ends")
.extend_from_slice(&data);
},
|err| log::error!("error capturing audio track: {:?}", err),
Some(Duration::from_millis(100)),
)
.context("failed to build input stream")?;
stream.play()?;
Ok(stream)
}
fn write_out(
samples: Arc<Mutex<Vec<i16>>>,
config: cpal::SupportedStreamConfig,
path: &Path,
) -> Result<()> {
let samples = std::mem::take(
&mut *samplesView on GitHub (pinned to f4178619ac)
Solutions
- Ensure the cpal Stream is dropped/stopped before write_out takes the samples lock
- If lock contention is legitimately possible, use lock() or handle the TryLockError instead of panicking in the realtime callback
- Document/enforce the lifecycle: capture -> stop stream -> write_out
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at crates/livekit_client/src/record.rs:70 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20).
Data as JSON: /api/errors/16efa84fdd9f8396.
Report an issue: GitHub.