linera-io/linera-protocol · error
invalid block header RLP
Error message
invalid block header RLP
What it means
Step 1 of process_deposit decodes the user-submitted block_header_rlp with proof::decode_block_header and expects success. The panic means the bytes are not valid RLP or do not deserialize into an EVM block header (wrong field structure/list encoding). The whole ProcessDeposit transaction aborts, so no state changes; this is an input-validation guard against malformed relayer submissions.
Source
Thrown at linera-bridge/contracts/evm-bridge/src/contract.rs:202
};
assert!(finalized, "block is not finalized");
log::info!("verified block hash 0x{hash_hex} is finalized");
}
async fn process_deposit(
&mut self,
block_header_rlp: &[u8],
receipt_rlp: &[u8],
proof_nodes: &[Vec<u8>],
tx_index: u64,
log_index: u64,
) {
let params = self.runtime.application_parameters();
// 1. Decode block header → (block_hash, receipts_root)
let (block_hash, receipts_root) =
proof::decode_block_header(block_header_rlp).expect("invalid block header RLP");
// 1b. Finality check: when an endpoint is configured, verify the block hash
// is finalized. Uses cached result if a previous deposit from this block
// was already processed.
if self.state.rpc_endpoint.get().is_empty() {
log::warn!("rpc_endpoint is empty — skipping block finality verification.");
} else if !self
.state
.verified_block_hashes
.contains(&block_hash.0)
.await
.expect("failed to check verified block hashes")
{
self.verify_block_hash(block_hash.0).await;
}
// 2. Verify receipt inclusion via MPT proof
let proof_bytes: Vec<Bytes> = proof_nodesView on GitHub (pinned to 6c226ddcb3)
Solutions
- Encode the header from a typed source: alloy_consensus::Header::decode(&rlp) locally before submitting, and submit exactly those bytes
- Fetch the header via eth_getBlockByHash with full transactions off and use the returned RLP-compatible encoding path your relayer already proved
- Check for hex/raw mixups: ensure you send RLP bytes, not a 0x-hex string
- Pin alloy versions between relayer and contract so header deserialization rules match
Example fix
// before (relayer sends whatever bytes it has)
let header_bytes = raw_from_feeder; // unvalidated
submit(ProcessDeposit { block_header_rlp: header_bytes, .. });
// after (prove it decodes before paying for the transaction)
use alloy_consensus::Header;
let header: Header = alloy_rlp::decode(&header_bytes)
.map_err(|e| anyhow!("invalid block header RLP: {e}"))?; // fail fast off-chain
submit(ProcessDeposit { block_header_rlp: header_bytes, .. }); Defensive patterns
Strategy: validation
Validate before calling
// Decode the header locally before submitting ProcessDeposit:
use alloy_consensus::Header;
fn header_rlp_valid(bytes: &[u8]) -> bool {
alloy_rlp::decode::<Header>(bytes).is_ok()
}
assert!(header_rlp_valid(&block_header_rlp), "do not submit: invalid block header RLP"); Type guard
fn valid_block_header_rlp(bytes: &[u8]) -> bool {
alloy_rlp::decode::<alloy_consensus::Header>(bytes).is_ok()
} Try / catch
// Contract-side panic aborts the operation atomically. Relayer-side, wrap
// the local decode in Result handling and quarantine the bad record:
match alloy_rlp::decode::<Header>(&bytes) {
Ok(h) => submit(ProcessDeposit { block_header_rlp: bytes, .. }),
Err(e) => { tracing::error!("bad header from feeder: {e}"); quarantine(bytes); } Prevention
- Always derive header bytes from eth_getBlockByHash rather than manual assembly
- Round-trip-check RLP through the same alloy version the contract uses
- Add fixtures in CI that assert decode_block_header succeeds for real mainnet headers
When it happens
Trigger: A relayer submits a truncated or byte-corrupted header; the header was RLP-encoded with a different library or a non-canonical encoding; hex vs raw-bytes confusion so the payload is a hex string rather than RLP; the header is from a non-EVM chain or pre-merge format the decoder rejects.
Common situations: Relayer pipelines that slice bytes by fixed offsets instead of using alloy/ethers Header types; passing blockHash instead of the header; double 0x-prefix stripping bugs; upgrades of alloy that tighten header deserialization.
Related errors
- failed to decode receipt logs
- block header must be an RLP list
- block header payload extends past available data
- failed to query chain ID from RPC endpoint
- receipt inclusion proof failed
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/b17484322d63d778.
Report an issue: GitHub.