embassy-rs/embassy · error
Failed to load PIO program
Error message
Failed to load PIO program: {:?} What it means
`PIO::load_program` is the panicking wrapper around `try_load_program`. It panics when the PIO instruction memory cannot accommodate the program at any (or the requested) origin — e.g. insufficient free instruction slots, or the program's fixed origin is already occupied/out of range.
Solutions
- Switch to `try_load_program` and handle the LoadError instead of panicking
- Unload existing programs or use a different PIO block with free instruction memory
- Reduce program size or remove an explicit `origin` so relocation can find space
- Verify which PIO instance is shared and whether another driver already loaded a program
Example fix
// before
let loaded = pio.load_program(&prog);
// after
let loaded = match pio.try_load_program(&prog) {
Ok(l) => l,
Err(e) => defmt::error!("pio load failed: {:?}", e),
}; Defensive patterns
Strategy: try-catch
Validate before calling
// ensure program fits: count used instruction memory first assert!(prog.code.len() <= 32, "program too large for PIO instr mem");
Try / catch
match pio.try_load_program(&prog) {
Ok(l) => l,
Err(LoadError::InsufficientFreeProgramMemory) => defmt::error!("PIO full"),
Err(e) => defmt::error!("load failed: {:?}", e),
} Prevention
- Use try_load_program in library/driver code; reserve load_program for examples
- Track which programs occupy each PIO block
- Avoid fixed origins unless relocation is impossible for your timing requirements
When it happens
Trigger: Loading a program whose SIZE exceeds free instruction memory; loading two programs that overlap; specifying `origin` where memory is already used; on RP2040 only 32 instruction slots exist per PIO so multiple large programs overflow.
Common situations: Loading a second PIO program into the same block without unloading the first; a program with a hard-coded origin conflicting with an auto-placed program; using a PIO block that cyw43 or another driver already claimed.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- pin is outside the PIO's GPIOBASE window .. ; use…
- All pins must either be <32 or >=16, in
- Invalid PIO_NO: , expected one of 0,1,2
- Invalid PIO_NO: , expected one of 0,1
- Can only take the executor once
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/4a87a557ee8ab40b.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-rp/src/pio/mod.rs:1216
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum LoadError {
/// Insufficient consecutive free instruction space to load program.
InsufficientSpace,
/// Loading the program would overwrite an instruction address already
/// used by another program.
AddressInUse(usize),
}
impl<'d, PIO: Instance> Common<'d, PIO> {
/// Load a PIO program. This will automatically relocate the program to
/// an available chunk of free instruction memory if the program origin
/// was not explicitly specified, otherwise it will attempt to load the
/// program only at its origin.
pub fn load_program<const SIZE: usize>(&mut self, prog: &Program<SIZE>) -> LoadedProgram<'d, PIO> {
match self.try_load_program(prog) {
Ok(r) => r,
Err(e) => panic!("Failed to load PIO program: {:?}", e),
}
}
/// Load a PIO program. This will automatically relocate the program to
/// an available chunk of free instruction memory if the program origin
/// was not explicitly specified, otherwise it will attempt to load the
/// program only at its origin.
pub fn try_load_program<const SIZE: usize>(
&mut self,
prog: &Program<SIZE>,
) -> Result<LoadedProgram<'d, PIO>, LoadError> {
match prog.origin {
Some(origin) => self.try_load_program_at(prog, origin).map_err(LoadError::AddressInUse),
None => {
// naively search for free space, allowing wraparound since
// PIO does support that. with only 32 instruction slots it
// doesn't make much sense to do anything more fancy.
let mut origin = 0;View on GitHub (pinned to 463a07b963)