linera-io/linera-protocol · error

invalid RLP item: {e}

Error message

invalid RLP item: {e}

What it means

skip_rlp_item's Header::decode failed at the current cursor: the next bytes are not a valid RLP item header (string or list). This helper walks block headers (decode_block_header) and receipt prefixes (decode_receipt_logs), so the error means the byte stream diverges from RLP structure at that point — corruption, truncation, or misaligned cursor.

Source

Thrown at linera-bridge/src/proof/mod.rs:536

    for (key, value) in &entries {
        builder.add_leaf(*key, value);
    }

    let root = builder.root();
    let proof_nodes = builder.take_proof_nodes();
    let proof = proof_nodes
        .matching_nodes_sorted(&target_key)
        .into_iter()
        .map(|(_, bytes)| bytes.to_vec())
        .collect();

    (root, proof)
}

/// Skips one RLP item (string or list) by reading its header and advancing past the payload.
fn skip_rlp_item(data: &mut &[u8]) -> Result<()> {
    let header = alloy_rlp::Header::decode(data).map_err(|e| anyhow!("invalid RLP item: {e}"))?;
    ensure!(
        data.len() >= header.payload_length,
        "not enough data to skip RLP item"
    );
    *data = &data[header.payload_length..];
    Ok(())
}

/// Decodes a single log entry from RLP.
///
/// Enforces the declared payload boundary: after decoding address, topics, and data,
/// verifies that exactly `payload_length` bytes were consumed.
fn decode_log(data: &mut &[u8]) -> Result<ReceiptLog> {
    let log_header =
        alloy_rlp::Header::decode(data).map_err(|e| anyhow!("invalid log RLP: {e}"))?;
    ensure!(log_header.list, "log must be an RLP list");
    ensure!(
        data.len() >= log_header.payload_length,

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Re-fetch the header/receipt from a trusted source and re-encode rather than patching bytes
  2. Verify the walking order matches the consensus field order for the chain type
  3. Dump the bytes around the failure offset to spot misalignment (previous payload_length vs actual item size)
Defensive patterns

Strategy: validation

Validate before calling

fn is_rlp_shaped(b: &[u8]) -> bool {
    if b.is_empty() { return false; }
    matches!(b[0], 0x80..=0xff) // any RLP prefix; 0x00..=0x7f is a single byte (valid item)
        || b[0] < 0x80
}

Try / catch

match decode_receipt_logs(&receipt_rlp) {
    Err(e) if e.to_string().contains("invalid RLP item") => {
        // cursor misalignment or corruption: re-fetch the source data
    }
    other => other?,
}

Prevention

When it happens

Trigger: Walking a block header or receipt where a previous field had a wrong payload_length (cursor misalignment), a truncated buffer, or non-RLP data passed as RLP. Note the follow-up ensure 'not enough data to skip RLP item' is the separate out-of-bounds case.

Common situations: Truncated or hex-corrupted bytes from storage or fixtures; version drift in header layouts between chains; skipping fields in the wrong order so the cursor lands mid-item.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/727fcf16cf2e38ca. Report an issue: GitHub.