{"record":{"id":"e05cac97e74a4f75","repo":"nautechsystems/nautilus_trader","slug":"invalid-fee-protocol0-new-e","errorCode":null,"errorMessage":"Invalid fee_protocol0_new '{}': {e}","messagePattern":"Invalid fee_protocol0_new '(.+?)': (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":1900,"sourceCode":"        let mut pool_identifiers: Vec<String> = Vec::with_capacity(len);\n        let mut blocks: Vec<i64> = Vec::with_capacity(len);\n        let mut transaction_hashes: Vec<String> = Vec::with_capacity(len);\n        let mut transaction_indices: Vec<i32> = Vec::with_capacity(len);\n        let mut log_indices: Vec<i32> = Vec::with_capacity(len);\n        let mut fee_protocol0s: Vec<i32> = Vec::with_capacity(len);\n        let mut fee_protocol1s: Vec<i32> = Vec::with_capacity(len);\n\n        // Fill vectors from updates\n        for update in updates {\n            chain_ids.push(chain_id as i32);\n            dex_names.push(update.dex.name.to_string());\n            pool_identifiers.push(update.pool_identifier.to_string());\n            blocks.push(update.block as i64);\n            transaction_hashes.push(update.transaction_hash.clone());\n            transaction_indices.push(update.transaction_index as i32);\n            log_indices.push(update.log_index as i32);\n            fee_protocol0s.push(i32::try_from(update.fee_protocol0_new).map_err(|e| {\n                anyhow::anyhow!(\n                    \"Invalid fee_protocol0_new '{}': {e}\",\n                    update.fee_protocol0_new\n                )\n            })?);\n            fee_protocol1s.push(i32::try_from(update.fee_protocol1_new).map_err(|e| {\n                anyhow::anyhow!(\n                    \"Invalid fee_protocol1_new '{}': {e}\",\n                    update.fee_protocol1_new\n                )\n            })?);\n        }\n\n        // Execute batch insert with UNNEST\n        sqlx::query(\n            \"\n            INSERT INTO pool_fee_protocol_update_event (\n                chain_id, dex_name, pool_identifier, block, transaction_hash, transaction_index,\n                log_index, fee_protocol0_new, fee_protocol1_new","sourceCodeStart":1882,"sourceCodeEnd":1918,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L1882-L1918","documentation":"Raised while preparing a batch insert of pool fee-protocol update events: the new fee-protocol value for token0 (`fee_protocol0_new`) could not be converted to `i32` via `i32::try_from`. This fires only if the value is out of the i32 range (e.g. a u64/usize field carrying a corrupt or sentinel value), and the TryFromIntError is wrapped with the offending value in the message. The whole batch is aborted before any SQL executes.","triggerScenarios":"A `FeeProtocolUpdate` with `fee_protocol0_new` outside i32::MIN..i32::MAX (typically a u64/usize field holding a garbage or sentinel value like u64::MAX from a decoding bug) is passed to the batch fee-protocol-update insert.","commonSituations":"Event decoding bugs assigning raw on-chain bytes to a numeric field; ABI version changes making the fee-protocol field wider or differently packed; sentinel/uninitialized values in upstream structs.","solutions":["Log and inspect the reported `fee_protocol0_new` value to find where the out-of-range number originates","Validate the value against the valid fee-protocol range (0..=255 in practice) before calling the insert","Check the event decoder/ABI parsing for fee_protocol0_new for width or offset bugs","Clamp or reject the event upstream instead of letting try_from fail mid-batch"],"exampleFix":"// before\nfee_protocol0s.push(i32::try_from(update.fee_protocol0_new).map_err(|e| {\n    anyhow::anyhow!(\"Invalid fee_protocol0_new '{}': {e}\", update.fee_protocol0_new)\n})?);\n// after: validate domain range first\nif update.fee_protocol0_new > 255 {\n    anyhow::bail!(\"fee_protocol0_new {} outside valid fee-protocol range\", update.fee_protocol0_new);\n}\nfee_protocol0s.push(i32::try_from(update.fee_protocol0_new)?);","handlingStrategy":"validation","validationCode":"fn validate_fee_protocol0(v: u64) -> anyhow::Result<()> {\n    anyhow::ensure!(v <= 255, \"fee_protocol0_new {v} outside fee-protocol range 0..=255\");\n    Ok(())\n}","typeGuard":"fn fits_i32(v: u64) -> bool { v <= i32::MAX as u64 }","tryCatchPattern":"match validate_fee_protocol0(update.fee_protocol0_new) {\n    Ok(()) => { /* proceed with insert */ },\n    Err(e) => { tracing::error!(\"skipping malformed fee-protocol update: {e}\"); }\n}","preventionTips":["Type fee-protocol fields as u8 in decoded event structs to make out-of-range unrepresentable","Validate decoded event fields against protocol domains before persisting","Log the raw offending value to catch decoder/ABI regressions early","Add unit tests around event decoding widths for fee-protocol fields"],"tags":["integer-overflow","type-conversion","validation"],"backgroundTag":"value-out-of-range","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}