embassy-rs/embassy · error
The last block has already been processed!
Error message
The last block has already been processed!
What it means
The payload phase refuses further blocks once the final block has been processed: after a call with last_block=true sets ctx.last_block_processed, any subsequent payload call panics. This protects the tag computation and CBC/ECB tail handling from corruption by post-final data.
Solutions
- Stop calling payload() once a last_block=true call returns; finalize and drop the context.
- Fix chunking so last_block=true is passed only for the actual final chunk.
- Create a new cipher context if more data genuinely remains.
Example fix
// before ctx.payload(final_chunk, &mut out, true); ctx.payload(leftover, &mut out2, true); // panics // after ctx.payload(final_chunk, &mut out, true); // verify tag; do not reuse ctx
Defensive patterns
Strategy: type-guard
Validate before calling
if !ctx.last_block_processed { ctx.payload(chunk, &mut out, is_last); } Type guard
fn can_process_payload(ctx: &CipherContext) -> bool { !ctx.last_block_processed } Prevention
- Compute last_block once from the chunk index in streaming loops
- Consume the context (move into a finalizer) after the last block
- Never retry payload calls without resetting state
When it happens
Trigger: Calling payload()/decrypt again after a call with last_block=true; accidentally passing last_block=true on a non-final chunk then continuing; restoring a saved context whose last_block_processed flag is already set.
Common situations: Streaming loops that don't break after the final chunk; off-by-one chunking producing a trailing empty call; retry logic re-invoking payload on a finished context.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Message is too large for given IV size.
- Cannot update AAD after starting payload!
- Additional associated data must be processed first!
- Output buffer length must match input length.
- Input length must be a multiple of
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/253f2a5d97c7b1f0.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/cryp/mod.rs:1416
self.load_context(ctx);
let last_block_remainder = input.len() % C::BLOCK_SIZE;
// Perform checks for correctness.
if !ctx.aad_complete && ctx.header_len > 0 {
panic!("Additional associated data must be processed first!");
} else if !ctx.aad_complete {
#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]
{
ctx.aad_complete = true;
T::regs().cr().modify(|w| w.set_crypen(false));
T::regs().cr().modify(|w| w.set_gcm_ccmph(2));
T::regs().cr().modify(|w| w.set_fflush(true));
T::regs().cr().modify(|w| w.set_crypen(true));
}
}
if ctx.last_block_processed {
panic!("The last block has already been processed!");
}
if input.len() > output.len() {
panic!("Output buffer length must match input length.");
}
if !last_block {
if last_block_remainder != 0 {
panic!("Input length must be a multiple of {} bytes.", C::BLOCK_SIZE);
}
}
if C::REQUIRES_PADDING {
if last_block_remainder != 0 {
panic!(
"Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.",
C::BLOCK_SIZE
);
}
}
if last_block {View on GitHub (pinned to 463a07b963)