embassy-rs/embassy · error

TRNG: consecutive health check failures. Increase…

Error message

TRNG: {} consecutive health check failures. Increase Config::sample_count.

What it means

The RP235x hardware TRNG runs entropy health checks on every random number generation. When the EHR valid flag never asserts and the health-check status reports failure MAX_HEALTH_CHECK_RETRIES times consecutively, the blocking wait gives up and panics, telling the user to raise `Config::sample_count`. Repeated health-check failures indicate the entropy source is producing insufficient/unhealthy samples for the current sample_count setting.

Solutions

  1. Increase `Config::sample_count` (raise it substantially, e.g. multiply by 2-10) so the health check accumulates enough entropy before validation
  2. Retry generation at a higher level with backoff, since failures can be transient under marginal conditions
  3. Check power supply and clock configuration — unstable analog supply degrades the entropy source and can cause persistent health-check failures
  4. If failures persist across sample_count increases, treat the TRNG as faulty and route through a software RNG seeded from a different source

Example fix

// before
let mut trng = Trng::new(p.TRNG, Config { sample_count: 100 });
let n = trng.blocking_next_u32();
// after
let mut trng = Trng::new(p.TRNG, Config { sample_count: 2000 });
let n = trng.blocking_next_u32();
Defensive patterns

Strategy: validation

Validate before calling

fn cfg_ok(cfg: &Config) -> Result<(), &'static str> {
    if cfg.sample_count < 1000 { Err("sample_count too low for reliable TRNG health checks") } else { Ok(()) }
}

Try / catch

// panics in no_std; validate config and use the async API with retry instead of blocking
defensive: tune sample_count upward until health checks pass reliably

Prevention

When it happens

Trigger: Calling `blocking_next_u32`, `blocking_next_u64`, or `blocking_fill_bytes` on the TRNG when the hardware health check fails MAX_HEALTH_CHECK_RETRIES consecutive times — usually because Config::sample_count is too low for the chip's noise source (e.g. default sample count too small at current clock/temperature conditions).

Common situations: RP235x boards with the TRNG configured with a small sample_count; noisy power/clock conditions weakening the ring-oscillator entropy; code copied from examples with aggressive (small) sample counts to speed up generation.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/f32008bfd7a0021f. Report an issue: GitHub.

Appendix: source

Thrown at embassy-rp/src/trng.rs:330

    /// Unmask all interrupt sources. The interrupt handler masks them when it fires.
    fn unmask_irq(&self) {
        self.info.regs.rng_imr().write(|w| {
            w.set_ehr_valid_int_mask(false);
            w.set_autocorr_err_int_mask(false);
            w.set_crngt_err_int_mask(false);
            w.set_vn_err_int_mask(false);
        });
    }

    fn blocking_wait_for_successful_generation(&self) {
        let regs = self.info.regs;
        let mut failures = 0;
        while regs.trng_valid().read().ehr_valid().not() {
            if self.handle_health_check_status() {
                failures += 1;
                if failures >= MAX_HEALTH_CHECK_RETRIES {
                    panic!(
                        "TRNG: {} consecutive health check failures. Increase Config::sample_count.",
                        MAX_HEALTH_CHECK_RETRIES
                    );
                }
            }
        }
    }

    /// Read out a completed block. Reading `EHR_DATA5` clears the result registers
    /// and starts the next generation.
    fn read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) {
        let regs = self.info.regs;
        let ehr_data_regs = [
            regs.ehr_data0(),
            regs.ehr_data1(),
            regs.ehr_data2(),
            regs.ehr_data3(),
            regs.ehr_data4(),

View on GitHub (pinned to 463a07b963)