{"record":{"id":"c65f56802cf6248e","repo":"linera-io/linera-protocol","slug":"invalid-topics-list-rlp-e","errorCode":null,"errorMessage":"invalid topics list RLP: {e}","messagePattern":"invalid topics list RLP: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/src/proof/mod.rs","lineNumber":567,"sourceCode":"fn decode_log(data: &mut &[u8]) -> Result<ReceiptLog> {\n    let log_header =\n        alloy_rlp::Header::decode(data).map_err(|e| anyhow!(\"invalid log RLP: {e}\"))?;\n    ensure!(log_header.list, \"log must be an RLP list\");\n    ensure!(\n        data.len() >= log_header.payload_length,\n        \"log payload extends past available data\"\n    );\n\n    // Limit reads to the declared payload boundary.\n    let mut log_data_buf = &data[..log_header.payload_length];\n    *data = &data[log_header.payload_length..];\n\n    let address = <Address as alloy_rlp::Decodable>::decode(&mut log_data_buf)\n        .map_err(|e| anyhow!(\"invalid log address: {e}\"))?;\n\n    // Decode topics list\n    let topics_header = alloy_rlp::Header::decode(&mut log_data_buf)\n        .map_err(|e| anyhow!(\"invalid topics list RLP: {e}\"))?;\n    ensure!(topics_header.list, \"topics must be an RLP list\");\n    ensure!(\n        log_data_buf.len() >= topics_header.payload_length,\n        \"topics payload extends past log boundary\"\n    );\n\n    let mut topics_data = &log_data_buf[..topics_header.payload_length];\n    log_data_buf = &log_data_buf[topics_header.payload_length..];\n\n    let mut topics = Vec::new();\n    while !topics_data.is_empty() {\n        let topic = <B256 as alloy_rlp::Decodable>::decode(&mut topics_data)\n            .map_err(|e| anyhow!(\"invalid topic: {e}\"))?;\n        topics.push(topic);\n    }\n\n    // Decode log data (byte string)\n    let data_header = alloy_rlp::Header::decode(&mut log_data_buf)","sourceCodeStart":549,"sourceCodeEnd":585,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/src/proof/mod.rs#L549-L585","documentation":"After the emitter address was consumed, the next field — the topics list — does not begin with a parsable RLP header. In a canonical log, topics form an RLP list of 32-byte strings; if the bytes following the address are not a list header, the entry is malformed or the address decode consumed the wrong number of bytes.","triggerScenarios":"decode_log with the address decoded but log_data_buf now starting mid-item: truncated payload after the address, topics encoded as a flat sequence instead of a nested list, or an address whose declared length did not match the canonical 20 bytes.","commonSituations":"Logs serialized by a tool that inlines topics without the outer list; payload_length that cuts off the topics header; fuzzed or hand-crafted proof inputs in tests.","solutions":["Validate the full log entry with alloy_primitives::Log::decode first; rejection there means the bytes are not a canonical log.","Verify the encoder wraps topics in an RLP list (outer header with list flag), not a concatenation of raw 32-byte strings.","Check that payload_length from the outer log header actually covers address + topics + data.","Regenerate the receipt proof from a trusted source."],"exampleFix":"// before: topics parsed with a custom header read\nlet topics_header = alloy_rlp::Header::decode(&mut log_data_buf)\n    .map_err(|e| anyhow!(\"invalid topics list RLP: {e}\"))?;\n\n// after: gate the whole entry through the reference decoder before custom parsing\nuse alloy_rlp::Decodable;\nanyhow::ensure!(\n    alloy_primitives::Log::decode(&mut &log_entry_bytes[..]).is_ok(),\n    \"topics section is not a valid RLP list — rejecting log\"\n);","handlingStrategy":"validation","validationCode":"use alloy_rlp::Decodable;\nfn log_has_valid_topics_list(rlp: &[u8]) -> bool {\n    alloy_primitives::Log::decode(&mut &rlp[..]).is_ok() // topics must be a list of B256\n}","typeGuard":null,"tryCatchPattern":"Err(e) if e.to_string().contains(\"invalid topics list RLP\") => reject_proof(e), // structural defect: no retry","preventionTips":["When building receipts, derive topics from alloy_sol_types event logs rather than manual RLP.","Fuzz the decoder boundary so malformed topics never panic downstream."],"tags":["rlp","ethereum","topics","decoding","bridge"],"backgroundTag":"rlp-decoding-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}