embassy-rs/embassy · error
invalid OUT length
Error message
invalid OUT length {} What it means
calc_out_len encodes an OUT endpoint's buffer size into the 16-bit USBRAM size field; the btable encoding only supports 2–60 bytes (small windows) or 61–1024 bytes (32-byte blocks). Requesting an OUT endpoint buffer outside 2..=1024 has no hardware encoding, so the driver panics at allocation time (via alloc_endpoint).
Solutions
- Use a max_packet_size between 2 and 1024 bytes for OUT endpoints (typically 8/16/32/64 for FS)
- Clamp requested sizes: size.clamp(2, 1024) before calling alloc_endpoint
- For 2048-byte HS needs, switch to a part/driver variant using usbram_32 (usb_host-style path) that supports larger sizes
- Check where the size value originates (descriptor parse, config constant) and validate it there
Example fix
// before let ep_out = dev.alloc_endpoint(EndpointType::Bulk, 0, 64)?; // IN=0 ok, OUT=0 panics if used for OUT let ep_out = dev.alloc_endpoint(EndpointType::Bulk, 2048, 2048)?; // OUT 2048 panics // after let ep_out = dev.alloc_endpoint(EndpointType::Bulk, 64, 64)?; // both within 2..=1024
Defensive patterns
Strategy: validation
Validate before calling
fn out_len_ok(len: u16) -> bool { (2..=1024).contains(&len) }
// before alloc_endpoint: assert!(out_len_ok(out_size)); Type guard
fn valid_out_size(len: u16) -> bool { (2..=1024).contains(&len) } Prevention
- Clamp OUT sizes to 2..=1024 before allocation
- Use standard FS packet sizes (8/16/32/64) unless you need larger
- Validate sizes parsed from descriptors instead of passing raw values
- Remember btable parts cap OUT buffers at 1024, not 2048
When it happens
Trigger: Calling usb_dev.alloc_endpoint(EndpointType::Bulk/Interrupt/Isochronous, max_packet_size = 0 or 1, or > 1024) on the btable-based usb.rs driver; also 0-byte OUT allocations fall into the wildcard arm.
Common situations: Passing wMaxPacketSize straight from a descriptor that is 0 or misparsed; requesting USB3-era 2048-byte packets on classic btable parts (max 1024); typos like 10 instead of 64 (still fine) vs 1 instead of 10 (panics).
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Endpoint memory full
- invalid OUT length
- USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but…
- USB clock should be 48Mhz but is
- USB HS PHY reference clock should be 19.2, 20 or 24 MHz but…
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/27bffa9f8bc9683b.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/usb/usb.rs:168
r.set_ctr_tx(true); // don't clear
r.set_dtog_rx(false); // don't toggle
r.set_dtog_tx(false); // don't toggle
r.set_stat_rx(Stat::from_bits(0));
r.set_stat_tx(Stat::from_bits(0));
r
}
fn align_len_up(len: u16) -> u16 {
((len as usize + USBRAM_ALIGN - 1) / USBRAM_ALIGN * USBRAM_ALIGN) as u16
}
// Returns (actual_len, len_bits)
fn calc_out_len(len: u16) -> (u16, u16) {
match len {
// NOTE: this could be 2..=62 with 16bit USBRAM, but not with 32bit. Limit it to 60 for simplicity.
2..=60 => (align_len_up(len), align_len_up(len) / 2 << 10),
61..=1024 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000),
_ => panic!("invalid OUT length {}", len),
}
}
#[cfg(not(any(usbram_32_2048, usbram_32_1024)))]
mod btable {
use super::*;
pub(super) fn write_in_tx<T: Instance>(index: usize, addr: u16) {
USBRAM.mem(index * 4 + 0).write_value(addr);
}
pub(super) fn write_in_rx<T: Instance>(index: usize, addr: u16) {
USBRAM.mem(index * 4 + 2).write_value(addr);
}
pub(super) fn write_in_len_rx<T: Instance>(index: usize, _addr: u16, len: u16) {
USBRAM.mem(index * 4 + 3).write_value(len);
}View on GitHub (pinned to 463a07b963)