{"record":{"id":"ed1da1a7304187d1","repo":"nautechsystems/nautilus_trader","slug":"position-lookback-validated-at-construction","errorCode":null,"errorMessage":"position lookback validated at construction","messagePattern":"position lookback validated at construction","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/live/src/execution/manager.rs","lineNumber":2709,"sourceCode":"                    .push(report.clone());\n            }\n        }\n\n        let keys = check\n            .client_coverage\n            .keys()\n            .copied()\n            .chain(venue_positions.iter().filter_map(|(key, reports)| {\n                reports\n                    .iter()\n                    .any(|report| report.signed_decimal_qty != Decimal::ZERO)\n                    .then_some(*key)\n            }))\n            .collect::<IndexSet<_>>();\n        let active_keys = keys.clone();\n        let query_end = self.clock.borrow().timestamp_ns();\n        let lookback = DurationNanos::try_from_mins(self.config.position_check_lookback_mins)\n            .expect(\"position lookback validated at construction\");\n        let query_start = query_end.saturating_sub(lookback);\n        let mut discrepancy_keys = IndexSet::new();\n        let mut queries = Vec::new();\n\n        for key in keys {\n            let coverage = check\n                .client_coverage\n                .entry(key)\n                .or_insert_with(|| Self::resolve_position_report_client_coverage(key, clients));\n            let prepared_revision = *check.activity_revisions.entry(key).or_default();\n            let venue_reports = venue_positions\n                .get(&key)\n                .map(Vec::as_slice)\n                .unwrap_or_default();\n            let comparison = self.position_quantity_comparison(key, venue_reports);\n            let tolerance = self.position_reconciliation_tolerance(key.1);\n\n            if comparison.quantities_match(tolerance) {","sourceCodeStart":2691,"sourceCodeEnd":2727,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/live/src/execution/manager.rs#L2691-L2727","documentation":"The periodic position check converts `config.position_check_lookback_mins` to `DurationNanos` and unwraps with `.expect(\"position lookback validated at construction\")`. The config constructor is supposed to have already validated the value; if a config object is built without that validation, `DurationNanos::try_from_mins` returns `Err` and this panics at runtime inside the periodic task. It is a deferred-invariant check, not a user-facing error.","triggerScenarios":"Constructing the live execution manager with a config assembled via raw struct literal (bypassing the validating constructor) where `position_check_lookback_mins` is zero, negative, or otherwise out of the range accepted by `DurationNanos::try_from_mins`; the panic fires when the position-check task first runs.","commonSituations":"Hand-editing config files with 0 or negative lookback; deserializing config from YAML/JSON where constructor validation is skipped; programmatic config assembly bypassing `new`/builder; copying an old config across a nautilus upgrade with changed validation rules.","solutions":["Set `position_check_lookback_mins` to a positive value (e.g. 60) in the live execution config","Construct the config through its validating constructor (`ExecutionEngineConfig::new`/builder) instead of a struct literal","Validate the lookback at startup with a clear error rather than panicking inside the periodic task","If the field is a float, ensure it is not NaN, since NaN also fails `try_from_mins`"],"exampleFix":"// before\nlet config = ExecutionEngineConfig { position_check_lookback_mins: 0.0, .. };\n// after\nlet config = ExecutionEngineConfig::new(/* ... */, 60.0); // validated at construction","handlingStrategy":"validation","validationCode":"let lookback_mins = config.position_check_lookback_mins;\nif !(lookback_mins > 0.0 && lookback_mins.is_finite()) {\n    return Err(anyhow::anyhow!(\"position_check_lookback_mins must be positive and finite\"));\n}\nDurationNanos::try_from_mins(lookback_mins)?;","typeGuard":"fn valid_lookback(mins: f64) -> bool { mins.is_finite() && mins > 0.0 }","tryCatchPattern":"let lookback = DurationNanos::try_from_mins(mins).map_err(|e| anyhow!(\"invalid lookback: {e}\"))?;","preventionTips":["Always build live execution config via its validating constructor","Validate config fields at startup, before spawning periodic tasks","After upgrades, re-validate stored config files against new constraints"],"tags":["config","panic","invariant"],"backgroundTag":"invalid-config-value","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"}