{"record":{"id":"df032ee44d443de9","repo":"nautechsystems/nautilus_trader","slug":"failed-to-retire-replaced-execution-hash-e","errorCode":null,"errorMessage":"Failed to retire replaced execution hash: {e}","messagePattern":"Failed to retire replaced execution hash: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":3816,"sourceCode":"        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to lock active execution intent {intent_id}: {e}\"))?\n        .ok_or_else(|| anyhow::anyhow!(\"Active execution intent {intent_id} was not found\"))?;\n        anyhow::ensure!(\n            execution_transition_allowed(&current_status, TransactionStatus::Replaced),\n            \"Invalid execution transition for intent {intent_id}: {current_status} -> replaced\"\n        );\n\n        sqlx::query(\n            \"\n            UPDATE execution_transaction_hash\n            SET current = FALSE, status = 'replaced', updated_at = NOW()\n            WHERE intent_id = $1 AND current\n            \",\n        )\n        .bind(intent_id)\n        .execute(&mut *transaction)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to retire replaced execution hash: {e}\"))?;\n\n        let row = sqlx::query_as::<_, ExecutionTransactionHashRow>(\n            \"\n            INSERT INTO execution_transaction_hash (\n                intent_id, chain_id, transaction_hash, status, current\n            ) VALUES ($1, $2, $3, 'replaced', TRUE)\n            ON CONFLICT (chain_id, transaction_hash) DO UPDATE\n            SET current = TRUE, updated_at = NOW()\n            WHERE execution_transaction_hash.intent_id = EXCLUDED.intent_id\n            RETURNING\n                id, intent_id, chain_id, transaction_hash, raw_transaction, status,\n                block_number, block_hash, receipt_success, gas_used,\n                effective_gas_price, current\n            \",\n        )\n        .bind(intent_id)\n        .bind(chain_id_db)\n        .bind(transaction_hash)","sourceCodeStart":3798,"sourceCodeEnd":3834,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/adapters/blockchain/src/cache/database.rs#L3798-L3834","documentation":"Wrapped sqlx error from the UPDATE execution_transaction_hash SET current = FALSE, status = 'replaced' WHERE intent_id = $1 AND current statement inside add_execution_replacement_hash (database.rs:3804-3816). This retires the intent's current hash row before inserting the replacement hash. Note it is not an error if zero rows match (a fresh intent may have no current row); this error means the UPDATE itself failed.","triggerScenarios":"Connection loss or timeout during the retire UPDATE; a CHECK constraint rejecting the 'replaced' status value; schema drift on the current/status/updated_at columns; the server aborting the transaction between the FOR UPDATE lock and this statement.","commonSituations":"Postgres failover mid-transaction; a status column CHECK/domain that predates the 'replaced' status value introduced by replacement support; migrations that added the current flag not applied.","solutions":["Downcast to sqlx::Error and read the database error to identify constraint vs connectivity causes","Apply migrations so execution_transaction_hash has current, status (with 'replaced' allowed), and updated_at","Retry the whole add_execution_replacement_hash call for transient classes - the transaction rolled back, so nothing was retired","If a status CHECK rejected 'replaced', widen the constraint in a migration"],"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), // constraint causes: fix schema/status value, do not retry\n}","preventionTips":["Include 'replaced' in any status CHECK constraint when migrating replacement support","Keep the current flag column indexed (intent_id, current) for cheap retirement","Run migrations atomically with code deploys so retire/upsert statements always match the schema"],"tags":["rust","sqlx","postgres","blockchain","transaction","replacement"],"backgroundTag":"database-update-failed","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}