{"record":{"id":"c972fe29faa3cc75","repo":"nautechsystems/nautilus_trader","slug":"fx-rollover-acknowledgement-count-expected","errorCode":null,"errorMessage":"FX rollover acknowledgement count {}, expected {}","messagePattern":"FX rollover acknowledgement count (.+?), expected (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/modules/fx_rollover.rs","lineNumber":665,"sourceCode":"        let day = day.as_mut().expect(\"rollover day initialized\");\n        day.pending_adjustments = Some(batch);\n        day.pending_end_date = Some(batch_end_date);\n        day.attempt_time = Some(ts_now);\n        Ok(SimulationModuleResult::Completed(adjustments))\n    }\n\n    fn acknowledge(&self, outcomes: &[AccountAdjustmentOutcome]) -> anyhow::Result<()> {\n        let (adjustments, attempt_time, batch_end_date) = {\n            let mut day = self.rollover_day.borrow_mut();\n            let day = day\n                .as_mut()\n                .ok_or_else(|| anyhow::anyhow!(\"FX rollover day is not initialized\"))?;\n            let adjustment_count = day\n                .pending_adjustments\n                .as_ref()\n                .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            )","sourceCodeStart":647,"sourceCodeEnd":683,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/modules/fx_rollover.rs#L647-L683","documentation":"BacktestNode's FX rollover module throws this when the number of acknowledgement outcomes supplied to `acknowledge` does not match the number of pending adjustments recorded in the completed rollover batch. Each adjustment must be explicitly acknowledged (success or failure) exactly once; a count mismatch indicates the caller returned partial or extra results from the rollover batch.","triggerScenarios":"Calling `process_rollover` when the FX rollover day has a completed `pending_adjustments` batch, and the outcomes Vec passed to `acknowledge` has fewer or more entries than `pending_adjustments.len()` — e.g. an adjustment handler silently skipped an instrument or duplicated results.","commonSituations":"Custom rollover adjustment callbacks that return early on error without emitting an outcome; applying adjustments to a filtered subset of instruments while acknowledging all; version drift where module internals changed but acknowledgement logic was not updated.","solutions":["Ensure every pending adjustment produces exactly one outcome entry in the same order","Check adjustment handlers for early returns that skip emitting an outcome","Log outcomes.len() and the adjustment count to identify which instruments are missing/duplicated","Pin module versions so rollover batch generation and acknowledgement logic stay in sync"],"exampleFix":"// before: skipping failed adjustments in outcomes\nlet outcomes = adjustments.iter().filter(|a| a.apply().is_ok()).map(...).collect();\n// after: one outcome per adjustment, including failures\nlet outcomes = adjustments.iter().map(|a| AckOutcome::from(a.apply())).collect();","handlingStrategy":"validation","validationCode":"assert_eq!(outcomes.len(), day.pending_adjustments.as_ref().map(|p| p.len()).unwrap_or(0), \"outcome/adjustment count mismatch\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Emit exactly one acknowledgement outcome per pending adjustment, in order","Include failures as outcomes rather than skipping them","Add a debug assertion comparing counts before calling acknowledge"],"tags":["backtest","fx-rollover","state-consistency"],"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"}