linera-io/linera-protocol · error
topics payload extends past log boundary
Error message
topics payload extends past log boundary
What it means
decode_log bounds every field read by the log's declared payload boundary. At linera-bridge/src/proof/mod.rs:569 it verifies the topics list's declared payload_length fits inside the remaining log payload before slicing topics_data. Failure means the topics header claims more bytes than the log's own boundary allows — an inconsistent inner length inside a truncated or malformed log.
Source
Thrown at linera-bridge/src/proof/mod.rs:569
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);
}
// Decode log data (byte string)
let data_header = alloy_rlp::Header::decode(&mut log_data_buf)
.map_err(|e| anyhow!("invalid log data RLP: {e}"))?;
ensure!(View on GitHub (pinned to 6c226ddcb3)
Solutions
- Rebuild the receipt/log via proper RLP encoding (alloy_rlp Encodable) so all nested length prefixes are computed, not hand-written.
- Verify the proof against receipts_root; discard and re-fetch nodes that fail structural checks.
- Add a structural sanity pass: outer log payload >= address item + topics header + topics payload before decoding.
- Log the three numbers (log payload_length, topics payload_length, remaining bytes) at failure to pinpoint which length is wrong.
Defensive patterns
Strategy: try-catch
Try / catch
match decode_receipt_logs(receipt_rlp) {
Ok(logs) => logs,
Err(e) if e.to_string().contains("topics payload extends past log boundary") => {
tracing::warn!(error = %e, "inconsistent nested lengths; rejecting node");
Vec::new()
}
Err(e) => return Err(e),
} Prevention
- Always build nested RLP with library encoders so inner lengths agree with outer boundaries.
- After editing any encoded structure, re-encode end to end rather than splicing bytes.
- Treat nested-length inconsistencies as corruption until proof verification says otherwise.
When it happens
Trigger: A log whose topics-list length prefix exceeds the space left after the address field; corrupt trie node bytes where one length field was edited but others were not; fixtures that enlarge topics without growing the outer log payload length.
Common situations: Partial writes or truncation of proof nodes; hand-maintained test RLP where the topics count changed but both the topics-list header and the log-list header were not updated; provider serving a garbled node.
Related errors
- log payload extends past available data
- log data extends past log boundary
- not enough data to skip RLP item
- log must be an RLP list
- topics must be an RLP list
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/f201484c6665cfb6.
Report an issue: GitHub.