linera-io/linera-protocol · error
invalid log address: {e}
Error message
invalid log address: {e} What it means
The RLP list header of a log entry parsed fine, but the first field inside it — the emitter address — did not decode. In Ethereum's log format (address, topics, data) the address must be an RLP string of exactly 20 bytes. Failure means the bytes at that position are not a 20-byte string, usually because the payload boundary or field alignment is wrong.
Source
Thrown at linera-bridge/src/proof/mod.rs:563
/// 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,
"log payload extends past available data"
);
// Limit reads to the declared payload boundary.
let mut log_data_buf = &data[..log_header.payload_length];
*data = &data[log_header.payload_length..];
let address = <Address as alloy_rlp::Decodable>::decode(&mut log_data_buf)
.map_err(|e| anyhow!("invalid log address: {e}"))?;
// Decode topics list
let topics_header = alloy_rlp::Header::decode(&mut log_data_buf)
.map_err(|e| anyhow!("invalid topics list RLP: {e}"))?;
ensure!(topics_header.list, "topics must be an RLP list");
ensure!(
log_data_buf.len() >= topics_header.payload_length,
"topics payload extends past log boundary"
);
let mut topics_data = &log_data_buf[..topics_header.payload_length];
log_data_buf = &log_data_buf[topics_header.payload_length..];
let mut topics = Vec::new();
while !topics_data.is_empty() {
let topic = <B256 as alloy_rlp::Decodable>::decode(&mut topics_data)
.map_err(|e| anyhow!("invalid topic: {e}"))?;
topics.push(topic);View on GitHub (pinned to 6c226ddcb3)
Solutions
- Cross-check the log bytes with alloy_primitives::Log::decode (or any reference decoder) — if it also rejects them, the producer is at fault.
- Hex-dump the first bytes of the sliced payload and verify a 0x94-prefixed 20-byte string (or 0x80 for empty) is present.
- Fix the encoder/prover that generated the receipt so logs use canonical [address, topics, data] ordering.
- Regenerate the proof from the chain instead of patching bytes.
Example fix
// before: parsing fields one by one from untrusted bytes
let address = <Address as alloy_rlp::Decodable>::decode(&mut log_data_buf)
.map_err(|e| anyhow!("invalid log address: {e}"))?;
// after: accept the log only if the reference decoder accepts the whole entry
use alloy_rlp::Decodable;
if alloy_primitives::Log::decode(&mut &log_entry_bytes[..]).is_err() {
anyhow::bail!("log payload is not a valid Ethereum log (address/topics/data)");
}
// safe to decode field-by-field now Defensive patterns
Strategy: validation
Validate before calling
use alloy_rlp::Decodable;
fn log_has_valid_address(rlp: &[u8]) -> bool {
alloy_primitives::Log::decode(&mut &rlp[..]).is_ok() // enforces 20-byte address
} Try / catch
match decode_receipt_logs(&bytes) {
Err(e) if e.to_string().contains("invalid log address") => {
return Ok(ProofOutcome::Rejected); // bad proof, not a transient error
}
other => other,
} Prevention
- Encode logs with alloy/alloy-consensus types instead of hand-rolled writers.
- In prover tests, assert the produced log decodes via the reference Log type.
When it happens
Trigger: decode_log after slicing data[..log_header.payload_length]: the inner buffer starts mid-item or is empty; payload_length declared by the header does not actually start with the address; the log list contains fields in a non-standard order.
Common situations: A producer that serializes logs as (topics, address, data) or omits the address; miscomputed payload_length in a hand-rolled encoder; test fixtures built with serde JSON-to-RLP converters instead of canonical encoding.
Related errors
- invalid log RLP: {e}
- invalid topics list RLP: {e}
- invalid log data RLP: {e}
- block header must be an RLP list
- receipt must be an RLP list
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/8666da6908a684bf.
Report an issue: GitHub.