{"record":{"id":"98f1207674c98fba","repo":"diem/diem","slug":"expected-same-number-of-event-lists-as-transaction","errorCode":null,"errorMessage":"expected same number of event lists as transactions, received {} event lists and {} transactions","messagePattern":"expected same number of event lists as transactions, received (.+?) event lists and (.+?) transactions","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"json-rpc/types/src/views.rs","lineNumber":885,"sourceCode":"            return Ok(Self::empty());\n        }\n        let start_version = txs\n            .first_transaction_version\n            .ok_or_else(|| format_err!(\"expected a start version since tx list non-empty\"))?;\n\n        let transactions = txs.transactions;\n        let transaction_infos = txs.proof.transaction_infos;\n\n        ensure!(\n            transaction_infos.len() == transactions.len(),\n            \"expected same number of transaction_infos as transactions, \\\n             received {} transaction_infos and {} transactions\",\n            transaction_infos.len(),\n            transactions.len(),\n        );\n\n        let event_lists = if let Some(event_lists) = txs.events {\n            ensure!(\n                event_lists.len() == transactions.len(),\n                \"expected same number of event lists as transactions, \\\n                 received {} event lists and {} transactions\",\n                event_lists.len(),\n                transactions.len(),\n            );\n            event_lists\n        } else {\n            vec![Vec::new(); transactions.len()]\n        };\n\n        let tx_iter = transactions.into_iter();\n        let infos_iter = transaction_infos.into_iter();\n        let event_lists_iter = event_lists.into_iter();\n\n        let iter = tx_iter.enumerate().zip(infos_iter).zip(event_lists_iter);\n\n        let tx_list = iter","sourceCodeStart":867,"sourceCodeEnd":903,"githubUrl":"https://github.com/diem/diem/blob/fc4714a8ea273b6efe8b13dbce72ea60aad9a16c/json-rpc/types/src/views.rs#L867-L903","documentation":"In the same view-conversion code, after checking transaction_infos, the library verifies that the optional `events` field contains one event list per transaction. A `TransactionsWithProof` whose event_lists count differs from the transaction count cannot prove which events belong to which transaction, so the library rejects it rather than silently mis-associating events.","triggerScenarios":"Converting a `TransactionsWithProof` into the verified view when `txs.events` is `Some` but `event_lists.len() != transactions.len()` — e.g. a node response where events were truncated, or a manually built struct that attached events for only some transactions.","commonSituations":"Node bug or partially synced state returning event lists for a subset of transactions; protocol-version mismatch between client and server changing how events are batched; hand-written test data where events were populated for fewer/more entries than transactions.","solutions":["Compare `events.as_ref().map(|e| e.len())` with `transactions.len()` before conversion and re-fetch if they differ","Re-query the node for transactions with proof and events; the response is incomplete or corrupt","If events are unnecessary, pass `None` (or use the non-events query variant) instead of a partially populated event list","When building fixtures, generate event_lists from the same loop that generates transactions so counts always match","Upgrade client/node to matching versions if the events proof format changed"],"exampleFix":"// before\nlet txs = TransactionsWithProof { transactions, events: Some(event_lists), proof };\nlet view = txs.try_into()?;\n// after\nif let Some(ev) = &event_lists {\n    assert_eq!(ev.len(), transactions.len(), \"event list count mismatch; re-fetch\");\n}\nlet txs = TransactionsWithProof { transactions, events: Some(event_lists), proof };\nlet view = txs.try_into()?;","handlingStrategy":"validation","validationCode":"fn validate_events(txs: &TransactionsWithProof) -> Result<(), String> {\n    if let Some(ev) = &txs.events {\n        if ev.len() != txs.transactions.len() {\n            return Err(format!(\"event lists ({}) != transactions ({})\", ev.len(), txs.transactions.len()));\n        }\n    }\n    Ok(())\n}","typeGuard":"fn has_matching_event_lists(txs: &TransactionsWithProof) -> bool {\n    match &txs.events {\n        Some(ev) => ev.len() == txs.transactions.len(),\n        None => true,\n    }\n}","tryCatchPattern":"match tx_view.try_into_transaction_with_proof() {\n    Ok(verified) => verified,\n    Err(e) if e.to_string().contains(\"expected same number of event lists\") => {\n        eprintln!(\"event list count mismatch, re-fetching\");\n        re_fetch_with_events()?  // returns the verified type\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Only populate `events` when the query actually requested events for all returned transactions","Pass None instead of a partially filled event list when events are not needed","Validate event list count against transaction count right after receiving the node response","Keep client SDK and node versions aligned to avoid events format drift"],"tags":["json-rpc","events","proof-validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"fc4714a8ea273b6efe8b13dbce72ea60aad9a16c","analyzedAt":"2026-09-04T21:07:05.890Z","contentChangedAt":"2026-09-04T21:07:05.890Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}