{"record":{"id":"ed398fb70942225f","repo":"nautechsystems/nautilus_trader","slug":"cannot-persist-position-event-with-no-position-id","errorCode":null,"errorMessage":"Cannot persist position event with no position_id: {}","messagePattern":"Cannot persist position event with no position_id: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/sql/queries.rs","lineNumber":1079,"sourceCode":"        .bind(event.commission.map(|commission| commission.to_string()))\n        .bind(event.reconciliation)\n        .bind(position_event_info)\n        .bind(\n            event\n                .causation_id\n                .map(|causation_id| causation_id.to_string()),\n        )\n        .bind(event.ts_event.to_string())\n        .bind(event.ts_init.to_string())\n        .execute(&mut **transaction)\n        .await\n        .map(|_| ())\n        .map_err(|e| anyhow::anyhow!(\"Failed to insert into position_event table: {e}\"))\n    }\n\n    fn event_position_id(event: &OrderFilled) -> anyhow::Result<PositionId> {\n        event.position_id.ok_or_else(|| {\n            anyhow::anyhow!(\n                \"Cannot persist position event with no position_id: {}\",\n                event.event_id\n            )\n        })\n    }\n\n    /// Inserts or updates an `AccountState` event via the provided `pool`.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the SQL INSERT or UPDATE operation fails.\n    pub async fn add_account(\n        pool: &PgPool,\n        updated: bool,\n        account_event: AccountState,\n    ) -> anyhow::Result<()> {\n        if updated {\n            let exists =","sourceCodeStart":1061,"sourceCodeEnd":1097,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/sql/queries.rs#L1061-L1097","documentation":"event_position_id extracts the Option<PositionId> from an OrderFilled event and errors when it is None, because a position_event row cannot be written without its position_id foreign reference. This is a data-integrity guard: only fills that are part of an opened position carry a position_id.","triggerScenarios":"insert_position_event is called with an OrderFilled whose position_id field is None — e.g. fills on orders that never opened a position (position_id was not set by the emulation/exec engine), or an OrderFilled reconstructed from a stream/fixture that dropped the field.","commonSituations":"Persisting fill events from venues or custom adapters that do not populate position_id; replaying events through a path that lost the position linkage; testing with hand-constructed OrderFilled fixtures where position_id was left as None.","solutions":["Check why the fill has no position: confirm the order opened/maintains a position and that the execution engine set position_id on the fill.","Filter out fills with position_id None before handing them to the database cache if they are not meant to be persisted as position events.","If constructing OrderFilled manually (tests/fixtures), set position_id to a valid PositionId.","Log and skip such events instead of failing the whole persistence batch if they are expected."],"exampleFix":"// before: cache errors on fills without a position\ncache.insert_position_event(&event).await?;\n\n// after: guard before persisting\nif let Some(_pos) = event.position_id {\n    cache.insert_position_event(&event).await?;\n} else {\n    log::warn!(\"Skipping persist, no position_id for event {}\", event.event_id);\n}","handlingStrategy":"type-guard","validationCode":"// caller-side check before persisting\nif event.position_id.is_none() {\n    log::warn!(\"fill {} has no position_id; not a position event\", event.event_id);\n}","typeGuard":"fn has_position(event: &OrderFilled) -> bool {\n    event.position_id.is_some()\n}","tryCatchPattern":"match cache.insert_position_event(&event).await {\n    Err(e) if e.to_string().contains(\"no position_id\") => {\n        log::warn!(\"skipping non-position fill {}: {e}\", event.event_id);\n    }\n    other => other?,\n}","preventionTips":["Only route OrderFilled events with a set position_id to the position-event persistence path.","Ensure your venue/adapter populates position_id on fills (open positions correctly before order submission).","In test fixtures, always set position_id on OrderFilled.","Validate event completeness at the boundary where events enter the persistence pipeline."],"tags":["data-integrity","position","missing-field","persistence"],"backgroundTag":"missing-required-argument","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"}