{"record":{"id":"00f53d6af989c03e","repo":"nautechsystems/nautilus_trader","slug":"cannot-persist-position-event-for-mismatched-po","errorCode":null,"errorMessage":"Cannot persist position event {} for mismatched position_id: expected {}, was {}","messagePattern":"Cannot persist position event (.+?) for mismatched position_id: expected (.+?), was (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/sql/queries.rs","lineNumber":850,"sourceCode":"                orders.push(order);\n            }\n        }\n        Ok(orders)\n    }\n\n    /// Replaces the fill event log for a `position_id` via the provided `pool`.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the fill is invalid or if the SQL operations fail.\n    pub async fn add_position(\n        pool: &PgPool,\n        position_id: PositionId,\n        event: &OrderFilled,\n    ) -> anyhow::Result<()> {\n        let event_position_id = Self::event_position_id(event)?;\n        if event_position_id != position_id {\n            anyhow::bail!(\n                \"Cannot persist position event {} for mismatched position_id: expected {}, was {}\",\n                event.event_id,\n                position_id,\n                event_position_id\n            );\n        }\n\n        let mut transaction = pool.begin().await?;\n\n        sqlx::query(r#\"DELETE FROM \"position_event\" WHERE position_id = $1\"#)\n            .bind(position_id.to_string())\n            .execute(&mut *transaction)\n            .await\n            .map(|_| ())\n            .map_err(|e| anyhow::anyhow!(\"Failed to delete position_event rows: {e}\"))?;\n\n        Self::insert_position_event(&mut transaction, event).await?;\n        transaction","sourceCodeStart":832,"sourceCodeEnd":868,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/sql/queries.rs#L832-L868","documentation":"add_position persists a position-opening event (OrderFilled) keyed by a PositionId. Before writing, it re-derives the position_id from the event via event_position_id and compares it with the caller-supplied position_id; on mismatch it bails rather than persisting an event under the wrong key, protecting the position event stream's integrity.","triggerScenarios":"Calling add_position with a position_id that differs from the one derivable from the OrderFilled event — e.g. passing a stale or wrong PositionId, reusing an event from a different position, or a client bug constructing position_id/instrument_id combinations inconsistently with the event.","commonSituations":"Backfill or replay code passing position IDs from a different venue/instrument than the fill event; mixing up position IDs when multiple instruments trade concurrently; custom persistence adapters forwarding events out of order or from the wrong position.","solutions":["Verify the caller derives the PositionId from the same OrderFilled event it passes (same instrument_id, venue, and position identity logic)","Check for stale/reused events: ensure the event belongs to the position you are adding (match event position/instrument ids before calling)","Log the expected vs actual position_id in the message and fix the origin of the mismatched ID (usually the strategy or reconciliation code)","If events come from a cache/reconciliation stream, confirm ordering so the fill that opens position X is not attributed to position Y"],"exampleFix":"// before: mismatched id -> bail\ncache.add_position(&stale_position_id, &fill_event)?;\n// after: derive id from the event itself\nlet position_id = PositionId::from(&fill_event);\ncache.add_position(&position_id, &fill_event)?;","handlingStrategy":"validation","validationCode":"// Rust: guard at the call site\nlet derived = PositionId::from(&fill_event);\nassert_eq!(derived, position_id, \"event position id mismatch before add_position\");","typeGuard":null,"tryCatchPattern":"if let Err(e) = cache.add_position(&position_id, &fill_event) {\n    if e.to_string().contains(\"mismatched position_id\") {\n        // re-derive id from the event and retry once\n        let pid = PositionId::from(&fill_event);\n        cache.add_position(&pid, &fill_event)?;\n    } else { return Err(e); }\n}","preventionTips":["Always derive PositionId from the same event you persist (PositionId::from(&event))","Avoid caching position IDs across instrument/venue changes; key them per instrument","In reconciliation/replay code, validate event ordering so fills are attributed to the correct position"],"tags":["database","persistence","position","validation"],"backgroundTag":"type-mismatch","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"}