{"record":{"id":"25406f5b726a20e7","repo":"nautechsystems/nautilus_trader","slug":"failed-to-persist-broadcast-attempt-for-transactio","errorCode":null,"errorMessage":"Failed to persist broadcast attempt for transaction {tx_hash}: {e}; the in-flight slot stays occupied","messagePattern":"Failed to persist broadcast attempt for transaction (.+?): (.+?); the in-flight slot stays occupied","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":1570,"sourceCode":"\n    /// Broadcasts the signed transaction and classifies the acceptance outcome.\n    async fn broadcast(&self, prepared: &PreparedTransaction) -> anyhow::Result<BroadcastOutcome> {\n        let tx_hash = prepared.tx_hash;\n\n        self.database\n            .record_execution_status(\n                prepared.intent_id,\n                &tx_hash.to_string(),\n                TransactionStatus::Broadcast,\n                None,\n                None,\n                None,\n                None,\n                None,\n            )\n            .await\n            .map_err(|e| {\n                anyhow::anyhow!(\n                    \"Failed to persist broadcast attempt for transaction {tx_hash}: {e}; the in-flight slot stays occupied\"\n                )\n            })?;\n\n        match self\n            .http_rpc_client\n            .send_raw_transaction(&prepared.raw_tx, &tx_hash)\n            .await\n        {\n            Ok(broadcast_hash) => {\n                if broadcast_hash != tx_hash {\n                    // The node acknowledged a different hash: acceptance of the signed\n                    // transaction is unverified, so reconcile through the persisted record\n                    // rather than poll a hash that cannot match the chain\n                    return Ok(BroadcastOutcome::Ambiguous(format!(\n                        \"Broadcast of transaction {tx_hash} returned a differing hash {broadcast_hash}; the persisted record reconciles instead of rebroadcasting\"\n                    )));\n                }","sourceCodeStart":1552,"sourceCodeEnd":1588,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/adapters/blockchain/src/execution/client.rs#L1552-L1588","documentation":"Immediately before broadcasting, the client records the 'Broadcast' status attempt via record_execution_status and that Postgres write failed ({e} carries the DB error). Because the write precedes send_raw_transaction, the transaction was not broadcast this attempt; the in-flight slot stays occupied so the intent cannot be double-processed, and reconciliation on the next connect completes the recovery.","triggerScenarios":"broadcast() hitting a DB error (connection loss, constraint failure, timeout) at the record_execution_status(TransactionStatus::Broadcast) call, before any RPC send is attempted.","commonSituations":"Transient Postgres outages at the worst moment; schema drift in the status-history table after a partial migration; connection pool exhaustion under concurrent strategy load.","solutions":["Reconnect the client after restoring database service: reconciliation loads the active intent, sees the signed hash, and safely re-drives broadcast/finality","Verify the intent row: SELECT status FROM execution_intent WHERE id = <intent_id>; it should still be 'signed', which reconciliation handles explicitly","Keep the same wallet and Postgres store configured so reconciliation finds the intent","Fix the underlying DB reliability issue (pool sizing, timeouts, HA) surfaced by {e}"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"-- Pre-trade DB liveness check (cheap canary the caller can run)\nSELECT 1 AS ok;\n-- Any timeout here means the broadcast-attempt persist will likely fail too; pause trading.","typeGuard":null,"tryCatchPattern":"try:\n    await submit_and_wait(order)\nexcept Exception as e:\n    if 'Failed to persist broadcast attempt' in str(e):\n        # tx was NOT broadcast this attempt; slot stays occupied on purpose.\n        # Restore DB, reconnect with the same wallet/store, and let reconciliation re-drive broadcast.\n        alert_ops('broadcast persist failure - reconnect to reconcile')\n        raise","preventionTips":["Monitor Postgres write latency and pause order flow when it degrades, since persist failures freeze the in-flight slot","Never manually clear the in-flight state after this error; reconciliation is the safe path"],"tags":["blockchain","postgres","write-failure","broadcast","reconciliation"],"backgroundTag":"database-write-failed","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}