{"record":{"id":"52fee280e482f9dc","repo":"nautechsystems/nautilus_trader","slug":"failed-to-record-replacement-transition-e","errorCode":null,"errorMessage":"Failed to record replacement transition: {e}","messagePattern":"Failed to record replacement transition: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":3863,"sourceCode":"        .bind(intent_id)\n        .execute(&mut *transaction)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to mark execution intent replaced: {e}\"))?;\n        sqlx::query(\n            \"\n            INSERT INTO execution_transaction_transition (\n                intent_id, transaction_hash_id, transition_key, from_status, to_status\n            ) VALUES ($1, $2, $3, $4, 'replaced')\n            ON CONFLICT (intent_id, transition_key) DO NOTHING\n            \",\n        )\n        .bind(intent_id)\n        .bind(row.id)\n        .bind(format!(\"replaced:{transaction_hash}\"))\n        .bind(current_status)\n        .execute(&mut *transaction)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to record replacement transition: {e}\"))?;\n\n        transaction\n            .commit()\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to commit replacement transaction: {e}\"))?;\n        Ok(row)\n    }\n\n    /// Marks one order event as emitted after dispatch.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the event kind is unknown, the intent is absent, the opposing\n    /// terminal marker is already set, or persistence fails.\n    pub async fn mark_execution_event_emitted(\n        &self,\n        intent_id: i64,\n        event: &str,","sourceCodeStart":3845,"sourceCodeEnd":3881,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/adapters/blockchain/src/cache/database.rs#L3845-L3881","documentation":"Wrapped sqlx error from the INSERT INTO execution_transaction_transition (intent_id, transaction_hash_id, transition_key, from_status, to_status) VALUES (..., 'replaced') ON CONFLICT (intent_id, transition_key) DO NOTHING statement in add_execution_replacement_hash (database.rs:3851-3863). It appends the audit record for the replacement transition; the ON CONFLICT key is 'replaced:{transaction_hash}', so replaying the same replacement never duplicates. The error is a database-level fault, not a duplicate-key issue.","triggerScenarios":"Connection loss or timeout during the insert; a foreign-key violation on transaction_hash_id or intent_id (should not occur in-transaction unless rows were concurrently deleted); schema drift on the transition table columns.","commonSituations":"Postgres restart between the intent status update and this insert; migrations creating execution_transaction_transition not applied; manual cleanup deleting hash rows while a replacement is recorded.","solutions":["Downcast and inspect the database error for FK or constraint details","Ensure the transition table exists with its (intent_id, transition_key) unique index by running migrations","Retry the whole call for transient causes - the rollback plus conflict key keep it safe","Avoid manually deleting execution_transaction_hash rows for intents that may still receive replacement events"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"fn is_transient_db_error(err: &anyhow::Error) -> bool {\n    err.downcast_ref::<sqlx::Error>().map_or(false, |e| {\n        matches!(e, sqlx::Error::PoolTimedOut | sqlx::Error::PoolClosed | sqlx::Error::Io(_))\n            || e.as_database_error().and_then(|d| d.code()).map_or(false, |c| {\n                matches!(c.as_ref(), \"40001\" | \"40P01\" | \"55P03\" | \"57014\" | \"08000\" | \"08003\" | \"08006\")\n            })\n    })\n}","tryCatchPattern":"match db.add_execution_replacement_hash(intent_id, chain_id, &hash).await {\n    Ok(row) => Ok(row),\n    Err(e) if is_transient_db_error(&e) => retry_with_backoff(e),\n    Err(e) => Err(e),\n}","preventionTips":["Never hand-delete execution_transaction_hash or transition rows for live intents","Keep the (intent_id, transition_key) unique index in place so DO NOTHING deduplication works","Ship transition-table migrations with the code that writes 'replaced' transitions"],"tags":["rust","sqlx","postgres","blockchain","audit-log","transaction"],"backgroundTag":"database-insert-failed","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}