{"record":{"id":"13ff96aee1a53645","repo":"nautechsystems/nautilus_trader","slug":"fx-rollover-batch-end-date-was-not-recorded","errorCode":null,"errorMessage":"FX rollover batch end date was not recorded","messagePattern":"FX rollover batch end date was not recorded","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/modules/fx_rollover.rs","lineNumber":681,"sourceCode":"                .ok_or_else(|| anyhow::anyhow!(\"no completed FX rollover batch to acknowledge\"))?\n                .len();\n            anyhow::ensure!(\n                outcomes.len() == adjustment_count,\n                \"FX rollover acknowledgement count {}, expected {}\",\n                outcomes.len(),\n                adjustment_count\n            );\n            let adjustments = day\n                .pending_adjustments\n                .take()\n                .ok_or_else(|| anyhow::anyhow!(\"no completed FX rollover batch to acknowledge\"))?;\n            (\n                adjustments,\n                day.attempt_time\n                    .take()\n                    .ok_or_else(|| anyhow::anyhow!(\"FX rollover attempt time was not recorded\"))?,\n                day.pending_end_date.ok_or_else(|| {\n                    anyhow::anyhow!(\"FX rollover batch end date was not recorded\")\n                })?,\n            )\n        };\n\n        let mut failed = Vec::new();\n        {\n            let mut totals = self.rollover_totals.borrow_mut();\n            let mut unapplied_totals = self.unapplied_rollover_totals.borrow_mut();\n\n            for (adjustment, outcome) in adjustments.into_iter().zip(outcomes) {\n                match outcome {\n                    AccountAdjustmentOutcome::Applied => {\n                        let total = totals.entry(adjustment.amount.currency).or_insert(0.0);\n                        *total += adjustment.amount.as_f64();\n                    }\n                    AccountAdjustmentOutcome::Failed(error) => {\n                        let kind = AccountAdjustmentFailureKind::from(error);\n                        let first_failure = self","sourceCodeStart":663,"sourceCodeEnd":699,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/modules/fx_rollover.rs#L663-L699","documentation":"Thrown when the rollover day's `pending_end_date` was not recorded although the acknowledge phase requires it. The end date is captured when the rollover batch begins; its absence with pending adjustments present means the batch state was assembled incompletely.","triggerScenarios":"`pending_adjustments` is Some but `pending_end_date` is None during `process_rollover`'s acknowledge phase — from partial state restore or code that starts a batch without recording the end date.","commonSituations":"Hand-built module state in tests; incomplete snapshot deserialization; broken custom rollover scheduling.","solutions":["Use the module's normal batch-start path so pending_end_date is recorded together with pending_adjustments","Fix snapshot/restore code to include pending_end_date","Verify no external code clears or fails to set pending_end_date"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"debug_assert!(day.pending_adjustments.is_none() || day.pending_end_date.is_some());","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Record pending_end_date whenever a batch is started","Include pending_end_date in snapshot serialization","Use the module lifecycle instead of direct state manipulation"],"tags":["backtest","fx-rollover","internal-state"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}