Summary
Using the ASIO host with a small fixed buffer size (16 frames), the ASIO
input/output stream panics after a few minutes of continuous playback with
attempt to add with overflow inside TimeBase::to_stream_instant
(src/host/asio/stream.rs:44). Only reproduces in debug builds (overflow
checks enabled); release builds presumably wrap silently instead.
Environment
- cpal: 0.18.2
- OS: Windows 11 Professional (10.0.26200 Build 26200)
- Interface: Focusrite USB ASIO (Focusrite Scarlett 2i2 3rd Gen)
- Rust toolchain: (
rustc 1.98.1 (48a229cea 2026-09-01))
- Profile: debug (
cargo run)
Minimal repro, using a Focusrite interface over ASIO with a 16-frame fixed
buffer, 44.1kHz, 2ch, i32, simply copying input channel 0 to both output
channels via a ring buffer:
use std::time::Duration;
use anyhow::anyhow;
use cpal::{BufferSize, StreamConfig};
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use ringbuf::consumer::Consumer;
use ringbuf::HeapRb;
use ringbuf::producer::Producer;
use ringbuf::traits::Split;
fn main() -> anyhow::Result<()> {
let host = cpal::host_from_id(cpal::HostId::Asio)?;
let devices = host.devices()?;
let focusrite = devices
.filter_map(|device| {
let description = device.description().ok()?;
if description.to_string().contains("Focusrite USB ASIO") {
Some(device)
} else {
None
}
})
.next()
.ok_or_else(|| anyhow!("Focusrite USB ASIO not found"))?;
println!("Using device: {}", focusrite.description()?);
let config = StreamConfig {
channels: 2,
sample_rate: 44_100,
buffer_size: BufferSize::Fixed(16),
};
println!("Config: {config:?}");
let ring = HeapRb::<i32>::new(16_384);
let (mut producer, mut consumer) = ring.split();
let input_stream = focusrite.build_input_stream(
config,
move |data: &[i32], _info| {
for frame in data.chunks(2) {
let input_1 = frame[0];
let _ = producer.try_push(input_1);
}
},
move |err| {
eprintln!("INPUT ERROR: {err}");
},
None,
)?;
let output_stream = focusrite.build_output_stream(
config,
move |data: &mut [i32], _info| {
for frame in data.chunks_mut(2) {
let sample = consumer.try_pop().unwrap_or(0);
frame[0] = sample;
frame[1] = sample;
}
},
move |err| {
eprintln!("OUTPUT ERROR: {err}");
},
None,
)?;
input_stream.play()?;
output_stream.play()?;
println!("Running...");
println!("Focusrite Input 1 -> Output 1");
println!("Press Ctrl+C to stop.");
loop {
std::thread::sleep(Duration::from_secs(1));
}
Ok(())
}
Cargo.toml:
[dependencies]
anyhow = "1.0.104"
cpal = { version = "0.18.2", features = ["asio"] }
ringbuf = "0.5.2"
Observed
After a few minutes of continuous audio, the input callback thread panics:
thread '<unnamed>' (8468) panicked at C:\Users\Zack\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\cpal-0.18.2\src\host\asio\stream.rs:44:13:
attempt to add with overflow
error: process didn't exit successfully: target\debug\guitar_host.exe (exit code: 0xc0000409, STATUS_STACK_BUFFER_OVERRUN)
Full stacktrace:
thread '<unnamed>' (3184) panicked at C:\Users\Zack\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\cpal-0.18.2\src\host\asio\stream.rs:44:13:
attempt to add with overflow
stack backtrace:
0: 0x7ff60f33f689 - std::backtrace_rs::backtrace::win64::trace
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\..\..\backtrace\src\backtrace\win64.rs:85
1: 0x7ff60f33f689 - std::backtrace_rs::backtrace::trace_unsynchronized
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\..\..\backtrace\src\backtrace\mod.rs:66
2: 0x7ff60f33f689 - std::sys::backtrace::_print_fmt
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\sys\backtrace.rs:74
3: 0x7ff60f33f689 - std::sys::backtrace::impl$0::print::impl$0::fmt
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\sys\backtrace.rs:44
4: 0x7ff60f34dca1 - core::fmt::write
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\core\src\fmt\mod.rs:0
5: 0x7ff60f342f24 - std::io::default_write_fmt
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\io\mod.rs:626
6: 0x7ff60f342f24 - std::io::Write::write_fmt<std::sys::stdio::windows::Stderr>
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\io\mod.rs:1730
7: 0x7ff60f337d85 - std::sys::backtrace::BacktraceLock::print
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\sys\backtrace.rs:47
8: 0x7ff60f337d85 - std::panicking::default_hook::closure$0
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\panicking.rs:292
9: 0x7ff60f33b5aa - std::panicking::default_hook
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\panicking.rs:319
10: 0x7ff60f33b7b3 - std::panicking::panic_with_hook
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\panicking.rs:815
11: 0x7ff60f337e94 - std::panicking::panic_handler::closure$0
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\panicking.rs:681
12: 0x7ff60f336b9f - std::sys::backtrace::__rust_end_short_backtrace<std::panicking::panic_handler::closure_env$0,never$>
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\sys\backtrace.rs:182
13: 0x7ff60f3384ee - std::panicking::panic_handler
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\std\src\panicking.rs:679
14: 0x7ff60f358d6d - core::panicking::panic_fmt
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\core\src\panicking.rs:80
15: 0x7ff60f358e9e - core::panicking::panic_const::panic_const_add_overflow
at /rustc/48a229ceaefd4985c50990b14116b6d856af0985/library\core\src\panicking.rs:175
16: 0x7ff60f2d1004 - cpal::host::asio::stream::TimeBase::to_stream_instant
at C:\Users\Zack\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\cpal-0.18.2\src\host\asio\stream.rs:44
17: 0x7ff60f2b8d23 - cpal::host::asio::stream::impl$3::build_input_stream_raw::closure$4<cpal::traits::DeviceTrait::build_input_stream::closure_env$0<cpal::platform::platform_impl::Device,i32,guitar_host::main::closure_env$2,guitar_host::main::closure_env$3>,guitar_host::main:
at C:\Users\Zack\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\cpal-0.18.2\src\host\asio\stream.rs:253
18: 0x7ff60f3232c3 - alloc::boxed::impl$32::call_mut<tuple$<ref$<asio_sys::bindings::CallbackInfo> >,dyn$<core::ops::function::FnMut<tuple$<ref$<asio_sys::bindings::CallbackInfo> >,assoc$<Output,tuple$<> > >,core::marker::Send>,alloc::alloc::Global>
at C:\Users\Zack\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\boxed.rs:2326
19: 0x7ff60f3286d8 - asio_sys::bindings::BufferCallback::run
at C:\Users\Zack\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\asio-sys-0.4.0\src\bindings\mod.rs:503
20: 0x7ff60f329c01 - asio_sys::bindings::buffer_switch_time_info
at C:\Users\Zack\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\asio-sys-0.4.0\src\bindings\mod.rs:1240
21: 0x22cadb25056 - DllUnregisterServer
22: 0x22cadb24812 - DllUnregisterServer
23: 0x22cadb239ec - DllUnregisterServer
24: 0x22cadb240ce - DllUnregisterServer
25: 0x22cadb226c6 - DllGetClassObject
26: 0x7fff60f1cd30 - wcsrchr
27: 0x7fff6237cd87 - BaseThreadInitThunk
28: 0x7fff6376caec - RtlUserThreadStart
error: process didn't exit successfully: `target\debug\guitar_host.exe` (exit code: 0xc0000409, STATUS_STACK_BUFFER_OVERRUN)
Expected
No panic; timestamps should remain correct (or at least not overflow) for
long-running streams, so it does not panic in dev mode (since release mode seems and sounds like it works just fine).
Summary
Using the ASIO host with a small fixed buffer size (16 frames), the ASIO
input/output stream panics after a few minutes of continuous playback with
attempt to add with overflowinsideTimeBase::to_stream_instant(
src/host/asio/stream.rs:44). Only reproduces in debug builds (overflowchecks enabled); release builds presumably wrap silently instead.
Environment
rustc 1.98.1 (48a229cea 2026-09-01))cargo run)Minimal repro, using a Focusrite interface over ASIO with a 16-frame fixed
buffer, 44.1kHz, 2ch, i32, simply copying input channel 0 to both output
channels via a ring buffer:
Cargo.toml:
Observed
After a few minutes of continuous audio, the input callback thread panics:
Full stacktrace:
Expected
No panic; timestamps should remain correct (or at least not overflow) for
long-running streams, so it does not panic in dev mode (since release mode seems and sounds like it works just fine).