{"record":{"id":"5d0d32a378577271","repo":"nautechsystems/nautilus_trader","slug":"persisted-swap-intent-has-no-instrument-id","errorCode":null,"errorMessage":"Persisted swap intent has no instrument ID","messagePattern":"Persisted swap intent has no instrument ID","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":838,"sourceCode":"                .client_order_id\n                .as_deref()\n                .ok_or_else(|| anyhow::anyhow!(\"Persisted swap intent has no client order ID\"))?,\n        )?;\n        let order = self\n            .core\n            .cache()\n            .try_order_owned(&client_order_id)\n            .with_context(|| {\n                format!(\n                    \"Cannot reconcile swap intent {} because order {client_order_id} is not restored\",\n                    intent.id\n                )\n            })?;\n        let instrument_id = InstrumentId::from_str(\n            intent\n                .instrument_id\n                .as_deref()\n                .ok_or_else(|| anyhow::anyhow!(\"Persisted swap intent has no instrument ID\"))?,\n        )?;\n        anyhow::ensure!(\n            order.instrument_id() == instrument_id,\n            \"Persisted swap instrument {instrument_id} does not match restored order instrument {}\",\n            order.instrument_id()\n        );\n        anyhow::ensure!(\n            intent.trader_id.as_deref() == Some(order.trader_id().as_str()),\n            \"Persisted swap trader does not match restored order\"\n        );\n        anyhow::ensure!(\n            intent.strategy_id.as_deref() == Some(order.strategy_id().as_str()),\n            \"Persisted swap strategy does not match restored order\"\n        );\n        anyhow::ensure!(\n            intent.account_id.as_deref() == Some(self.core.account_id.as_str()),\n            \"Persisted swap account does not match execution client account\"\n        );","sourceCodeStart":820,"sourceCodeEnd":856,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/adapters/blockchain/src/execution/client.rs#L820-L856","documentation":"restore_swap_plan (crates/adapters/blockchain/src/execution/client.rs:838) parses the persisted swap intent's instrument_id column; a NULL value aborts reconciliation. The instrument id (e.g. WETH-USDT-3000.UNISWAP_V3) is required to resolve the pool and validate the restored order against the intent. Swap intents are always persisted with it, so NULL means the durable row is incomplete - legacy schema data, manual edits, or a torn write - and the client fails closed instead of reconciling against unknown markets.","triggerScenarios":"An active swap intent row with NULL instrument_id encountered at connect()-time reconciliation; intent rows migrated from an older schema that lacked the column values; direct SQL manipulation of the intent table.","commonSituations":"Restoring a database backup taken with an older crate version; bulk-editing or ETL scripts that null out 'optional-looking' columns.","solutions":["Inspect the intent row and its transaction hashes: SELECT id, status, instrument_id FROM execution_intent WHERE id = <id>.","Confirm the transaction's on-chain outcome, then mark the intent resolved or remove it so reconciliation proceeds.","Re-run the deployment that writes instrument_id on every swap intent (current EXECUTION_SCHEMA_VERSION).","Audit for other NULL columns in the same row (client_order_id, trader_id, pool_address, amount_in) - they fail subsequent checks too."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"-- Pre-flight: active swap intents missing required identity columns\nSELECT id, status\nFROM execution_intent\nWHERE status IN ('prepared', 'signed', 'submitted')\n  AND purpose = 'swap'\n  AND instrument_id IS NULL;","typeGuard":"fn is_missing_instrument_id(e: &anyhow::Error) -> bool {\n    e.to_string().contains(\"Persisted swap intent has no instrument ID\")\n}","tryCatchPattern":"if let Err(e) = client.connect().await {\n    if is_missing_instrument_id(&e) {\n        // incomplete durable row: stop and repair the intent record before trading\n        log::error!(\"cannot reconcile swap intent without instrument: {e}\");\n    }\n    return Err(e);\n}","preventionTips":["Audit active intents for NULL identity columns before every version upgrade.","Reject database restores that carry intents from older schemas into a live deployment.","Ensure swap submission paths always persist instrument_id alongside the intent."],"tags":["reconciliation","postgres","instrument","data-integrity","rust"],"backgroundTag":"missing-required-field","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}