FuelLabs/fuel-core · error · anyhow::Error

Relayer is too far out of sync

Error message

Relayer is too far out of sync

What it means

Thrown by MaybeRelayerAdapter::await_until_if_in_range (crates/fuel-core/src/service/adapters/consensus_module.rs:72), the RelayerPort used during block production/import when the relayer feature is enabled. It computes da_height - relayer_finalized_da_height and requires the gap to stay within max_da_lag; a larger gap fails fast instead of waiting on await_at_least_synced, because the relayer may never reach the requested DA height.

Source

Thrown at crates/fuel-core/src/service/adapters/consensus_module.rs:72

    }

    fn block_header_merkle_root(&self, height: &BlockHeight) -> StorageResult<Bytes32> {
        self.storage::<FuelBlocks>().root(height).map(Into::into)
    }
}

#[async_trait::async_trait]
impl RelayerPort for MaybeRelayerAdapter {
    async fn await_until_if_in_range(
        &self,
        da_height: &DaBlockHeight,
        _max_da_lag: &DaBlockHeight,
    ) -> anyhow::Result<()> {
        #[cfg(feature = "relayer")]
        {
            if let Some(sync) = &self.relayer_synced {
                let current_height = sync.get_finalized_da_height();
                anyhow::ensure!(
                    da_height.saturating_sub(*current_height) <= **_max_da_lag,
                    "Relayer is too far out of sync"
                );
                sync.await_at_least_synced(da_height).await?;
            }
            Ok(())
        }
        #[cfg(not(feature = "relayer"))]
        {
            anyhow::ensure!(
                **da_height == 0,
                "Cannot have a da height above zero without a relayer"
            );
            Ok(())
        }
    }
}

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Wait for the relayer to catch up (watch its sync logs/metrics) and let block production retry
  2. Point the node at a faster, reliable Ethereum RPC endpoint for the relayer
  3. Increase max_da_lag in the PoA consensus config if a larger, controlled DA lag is acceptable
  4. If the block's da_height came from custom producer/test code, clamp it to a realistic finalized DA height

Example fix

// before (custom block production):
//   header.set_da_height(DaBlockHeight(15_000_000)); // eth mainnet tip
// after:
//   let finalized = relayer.get_finalized_da_height();
//   header.set_da_height(finalized + DaBlockHeight(1));
Defensive patterns

Strategy: retry

Validate before calling

// before triggering production for a da_height:
let finalized = relayer_sync.get_finalized_da_height();
let lag = da_height.saturating_sub(finalized);
if lag > max_da_lag {
    // wait/retry later instead of calling into production now
}

Try / catch

loop {
    match relayer_port.await_until_if_in_range(&da_height, &max_da_lag).await {
        Err(e) if e.to_string().contains("too far out of sync") => {
            tokio::time::sleep(Duration::from_secs(10)).await; // relayer still catching up
        }
        other => break other,
    }
}

Prevention

When it happens

Trigger: Producing or importing a block whose header da_height exceeds sync.get_finalized_da_height() by more than the max_da_lag passed by the PoA consensus module.

Common situations: Relayer lagging due to a slow or rate-limited Ethereum RPC endpoint; freshly started relayer syncing the DA chain from the beginning; a manually crafted or test-produced block with an inflated da_height; max_da_lag left at a small default in the consensus config.

Related errors


AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16). Data as JSON: /api/errors/105dddd660b31ffc. Report an issue: GitHub.