nautechsystems/nautilus_trader · error · anyhow::Error

Failed to reconstruct migrated intent: {e}

Error message

Failed to reconstruct migrated intent: {e}

What it means

This error wraps sqlx failures from `UPDATE execution_intent SET status = $2, updated_at = NOW() WHERE id = $1`, the step that moves the locked migrated intent to its terminal status (finalized/reverted) after the hash row was reconstructed. It fires only when the UPDATE errors at the database level — not when it affects zero rows, since the intent row was already locked and verified earlier in the same transaction. The driver error is interpolated as {e}.

Source

Thrown at crates/adapters/blockchain/src/cache/database.rs:4285

                        .bind(block_hash)
                        .bind(receipt_success)
                        .bind(gas_used)
                        .bind(record.effective_gas_price.as_deref())
                        .execute(&mut *transaction)
                        .await
                        .map_err(|e| anyhow::anyhow!("Failed to reconstruct migrated hash: {e}"))?;
                        anyhow::ensure!(
                            hash_result.rows_affected() == 1,
                            "Migrated terminal transaction hash was not found"
                        );
                        sqlx::query(
                            "UPDATE execution_intent SET status = $2, updated_at = NOW() WHERE id = $1",
                        )
                        .bind(record.intent_id)
                        .bind(status.as_str())
                        .execute(&mut *transaction)
                        .await
                        .map_err(|e| anyhow::anyhow!("Failed to reconstruct migrated intent: {e}"))?;
                        if current_status != status.as_str() {
                            sqlx::query(
                                "
                                INSERT INTO execution_transaction_transition (
                                    intent_id, transaction_hash_id, transition_key,
                                    from_status, to_status, block_number, block_hash
                                )
                                SELECT $1, id, $3, $4, $5, $6, $7
                                FROM execution_transaction_hash
                                WHERE intent_id = $1 AND transaction_hash = $2
                                ",
                            )
                            .bind(record.intent_id)
                            .bind(transaction_hash)
                            .bind(format!("migration:{}:{transaction_hash}", status.as_str()))
                            .bind(current_status)
                            .bind(status.as_str())
                            .bind(block_number)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the interpolated {e}; constraint violations name the offending column/constraint directly.
  2. Verify execution_intent.status accepts TransactionStatus::Finalized/Reverted as_str() values; align enum strings or schema.
  3. Check for transaction-aborted cascade: an earlier statement in this transaction failed; fix that first error in the logs.
  4. Retry the migration if the cause was a transient connection failure; the rollback keeps state consistent.
  5. Ensure no concurrent schema changes run during the migration window.

Example fix

// before: status passed straight through
.bind(status.as_str())

// after: guard against schema drift first
let s = status.as_str();
anyhow::ensure!(matches!(s, "finalized" | "reverted"), "unexpected terminal status {s}");
.bind(s)
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm the terminal status strings are accepted by the schema
let allowed: Vec<String> = sqlx::query_scalar(
    "SELECT unnest(enum_range(NULL::execution_intent_status))::text",
).fetch_all(&pool).await?;
anyhow::ensure!(allowed.contains(&"finalized".to_string()) && allowed.contains(&"reverted".to_string()),
    "schema does not accept terminal statuses: {allowed:?}");

Type guard

fn is_terminal_status(status: &TransactionStatus) -> bool {
    matches!(status, TransactionStatus::Finalized | TransactionStatus::Reverted)
}

Try / catch

if let Err(e) = run_migration(&pool, records).await {
    let msg = format!("{e:#}");
    if msg.contains("Failed to reconstruct migrated intent") {
        // usually schema drift on execution_intent.status or an aborted transaction
        tracing::error!("intent UPDATE failed: {msg}");
    }
    return Err(e); // rolled back; safe to fix and re-run
}

Prevention

When it happens

Trigger: The UPDATE fails due to: a CHECK constraint or enum on the status column rejecting the terminal status string, the transaction already being aborted by a prior failed statement, a connection loss mid-transaction, or conflicting locks from concurrent DDL.

Common situations: Schema drift where execution_intent.status allows different values than TransactionStatus::as_str() produces; DB connection timeout during a long migration batch; migration running while another deployment applies schema changes.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/8206f51446285761. Report an issue: GitHub.