embassy-rs/embassy · critical
BufferedUarte UART overrun
Error message
BufferedUarte UART overrun
What it means
The BufferedUarte interrupt handler reads the UARTE ERRORSRC register after an error event and panics if the overrun bit is set, meaning a received byte arrived while the RX FIFO/DMA path was full and was lost. Because data was irrecoverably dropped, the driver treats overrun as fatal rather than silently returning truncated input.
Solutions
- Increase the internal buffer size passed when creating the BufferedUarte (and spawn the reader task with higher priority)
- Ensure a task continuously calls `read()` so the buffer is drained promptly; avoid long-lived locks or blocking work on the reader task
- Enable hardware flow control (CTS) so the peer stops sending when the buffer fills
- Lower the baud rate to match the achievable processing throughput
Example fix
// before let mut rx_buf = [0u8; 32]; let mut uarte = BufferedUarte::new(p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.PIN_8, p.PIN_11, ParityBit::NONE, Parity::EXCLUDED, Baudrate::BAUD1M, &mut rx_buf); // after static RX_BUFFER: StaticCell<[u8; 1024]> = StaticCell::new(); let rx_buf = &mut *RX_BUFFER.init([0u8; 1024]); let mut uarte = BufferedUarte::new(p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.PIN_8, p.PIN_11, ParityBit::NONE, Parity::EXCLUDED, Baudrate::BAUD1M, rx_buf);
Defensive patterns
Strategy: try-catch
Try / catch
// Overrun panics in the IRQ handler; it cannot be caught. Guard by sizing buffers
// and draining continuously:
// loop {
// let n = uarte.read(&mut buf).await?; // keep the consumer always pending on read
// process(&buf[..n]);
// }
// If the panic is fatal, validate throughput: baud_rate / 10 bytes-per-sec must be
// well below buffer_size drained per scheduling period. Prevention
- Size the RX buffer generously for the baud rate (bytes/sec ≈ baud/10)
- Keep a dedicated high-priority task permanently pending on `read()`
- Enable RTS/CTS hardware flow control where the peer supports it
- Avoid blocking or long-running work on the UART reader task
When it happens
Trigger: Calling any read API on `BufferedUarte` (or using it via embedded-io traits) while the UART receives bytes faster than the task drains the internal buffer; the ERROR event fires with `errorsrc().overrun() == 1` inside `on_interrupt`.
Common situations: High baud rates (e.g. 1M+ baud) with a small shared read buffer; a blocked consumer task that never calls `read()` while the peer streams continuously; `low_power`/wake configurations where DMA restart is delayed; missing flow control (CTS/RTS) on a fast link.
Related errors
- RTS and CTS pins must be either both set or none set.
- Failed to apply workaround for UART
- Boot prepare error
- DMA data error
- CRACEN RNG health test failed
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/de315c71debbac6e.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-nrf/src/buffered_uarte/v2.rs:89
}
impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for InterruptHandler<U> {
unsafe fn on_interrupt() {
let r = U::regs();
let ss = U::state();
let s = U::buffered_state();
if let Some(mut rx) = unsafe { s.rx_buf.try_writer() } {
let buf_len = s.rx_buf.len();
let half_len = buf_len / 2;
if r.events_error().read() != 0 {
r.events_error().write_value(0);
let errs = r.errorsrc().read();
r.errorsrc().write_value(errs);
if errs.overrun() {
panic!("BufferedUarte UART overrun");
}
}
if r.events_dma().rx().end().read() != 0 {
//trace!(" irq_rx: endrx");
r.events_dma().rx().end().write_value(0);
if s.rx_started.swap(false, Ordering::Relaxed) {
// Received some bytes, wake task.
let rxed = r.dma().rx().amount().read().amount() as usize;
rx.push_done(rxed);
ss.rx_waker.wake();
}
}
if !s.rx_started.load(Ordering::Relaxed) {
let (ptr, len) = rx.push_buf();
View on GitHub (pinned to 463a07b963)