{"record":{"id":"b6ca4727ce885849","repo":"nautechsystems/nautilus_trader","slug":"replacement-scan-cursor-number-is-negative","errorCode":null,"errorMessage":"Replacement scan cursor number is negative","messagePattern":"Replacement scan cursor number is negative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":6215,"sourceCode":"              AND s.wallet_address = $3\n              AND s.nonce = $4\n            \",\n        )\n        .bind(intent_id)\n        .bind(chain_id)\n        .bind(wallet_address)\n        .bind(nonce)\n        .fetch_optional(&self.pool)\n        .await\n        .context(\"failed to load replacement scan cursor\")?;\n        row.map(\n            |(number, hash, parent_hash, timestamp, base_fee, stored_digest)| {\n                anyhow::ensure!(\n                    stored_digest == manifest_digest,\n                    \"Replacement scan manifest identity changed\"\n                );\n                Ok(ExecutionVerifiedHeader {\n                    number: u64::try_from(number)\n                        .context(\"Replacement scan cursor number is negative\")?,\n                    hash,\n                    parent_hash,\n                    timestamp: u64::try_from(timestamp)\n                        .context(\"Replacement scan cursor timestamp is negative\")?,\n                    base_fee_per_gas: base_fee\n                        .map(|value| {\n                            value.parse::<u128>().map_err(|_| {\n                                anyhow::anyhow!(\"Replacement scan cursor base fee is invalid\")\n                            })\n                        })\n                        .transpose()?,\n                })\n            },\n        )\n        .transpose()\n    }\n","sourceCodeStart":6197,"sourceCodeEnd":6233,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L6197-L6233","documentation":"The replacement scan cursor stores the block number as a signed i64 BIGINT. When converting it back to the u64 ExecutionVerifiedHeader.number, a negative value cannot be represented and this error is raised.","triggerScenarios":"The execution_verified_finalized_header.number column (or the joined scan row) contains a negative i64, and load_execution_replacement_cursor attempts u64::try_from(number).","commonSituations":"Manual database edits or corrupted rows; a buggy writer inserting sign-flipped values; type confusion when another tool wrote the table.","solutions":["Inspect the offending row's number value and correct or delete the corrupted row","Audit the writer path that inserted the negative block number","Re-run the replacement scan from a known-good finalized header"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let number: i64 = /* loaded cursor number */;\nif number < 0 {\n    // treat row as corrupt: delete and rescan\n}","typeGuard":"fn valid_block_number(n: i64) -> bool { n >= 0 }","tryCatchPattern":"match db.load_execution_replacement_cursor(..).await {\n    Err(e) if e.to_string().contains(\"cursor number is negative\") => {\n        // purge corrupt row and re-record the scan\n    }\n    other => other,\n}","preventionTips":["Never hand-edit database rows for cursors","Add CHECK (number >= 0) constraints on header tables","Re-scan from a known-good finalized header after corruption is detected"],"tags":["database","integer-overflow","data-corruption"],"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"}