nautechsystems/nautilus_trader · error · anyhow::Error

Failed to reconstruct migrated hash: {e}

Error message

Failed to reconstruct migrated hash: {e}

What it means

This error wraps sqlx failures from the UPDATE of execution_transaction_hash that reconstructs a migrated terminal transaction hash (status, block number/hash, receipt_success, gas_used, effective_gas_price). It fires only when the UPDATE statement errors at the database level; the separate 'Migrated terminal transaction hash was not found' error covers the zero-rows-affected case. The driver error is interpolated as {e}.

Source

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

                            UPDATE execution_transaction_hash
                            SET status = $3, block_number = $4, block_hash = $5,
                                receipt_success = $6, gas_used = $7,
                                effective_gas_price = $8, updated_at = NOW()
                            WHERE intent_id = $1 AND transaction_hash = $2 AND current
                              AND payload_expected
                            ",
                        )
                        .bind(record.intent_id)
                        .bind(transaction_hash)
                        .bind(status.as_str())
                        .bind(block_number)
                        .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

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the interpolated {e} to identify the failing column or constraint and fix the offending migration record field.
  2. Verify the target schema matches the migration's expectations (execution_transaction_hash columns: status, block_number, block_hash, receipt_success, gas_used, effective_gas_price).
  3. Confirm numeric fields fit their column types (BIGINT bounds for block_number/gas_used) and price strings parse as NUMERIC before migrating.
  4. Re-run the migration if the error was transient (connection drop); the transaction rollback preserves consistency.
  5. Check the DB server logs at the failure time for constraint or serialization details.

Example fix

// before: binding unvalidated values
.bind(record.effective_gas_price.as_deref())

// after: validate before executing
let price = record.effective_gas_price.as_deref()
    .map(|p| p.parse::<rust_decimal::Decimal>())
    .transpose()
    .context("invalid effective_gas_price in migration record")?;
.bind(price)
Defensive patterns

Strategy: validation

Validate before calling

// before migrating, confirm the hash row is updatable and values fit columns
let row = sqlx::query_scalar::<_, i64>(
    "SELECT COUNT(*) FROM execution_transaction_hash \
     WHERE intent_id = $1 AND transaction_hash = $2 AND current AND payload_expected",
).bind(&record.intent_id).bind(&record.transaction_hash)
 .fetch_one(&pool).await?;
anyhow::ensure!(row == 1, "hash row not updatable for intent {}", record.intent_id);

Type guard

fn hash_fields_in_range(record: &MigrationRecord) -> bool {
    record.block_number.map_or(false, |b| i64::try_from(b).is_ok())
        && record.gas_used.map_or(false, |g| i64::try_from(g).is_ok())
        && record.effective_gas_price.as_deref().map_or(true, |p| p.parse::<rust_decimal::Decimal>().is_ok())
}

Try / catch

if let Err(e) = run_migration(&pool, records).await {
    let msg = format!("{e:#}");
    if msg.contains("Failed to reconstruct migrated hash") {
        tracing::error!("hash UPDATE failed at DB level, check schema/constraints: {msg}");
    }
    return Err(e); // transaction rolled back; state consistent
}

Prevention

When it happens

Trigger: The UPDATE `... WHERE intent_id = $1 AND transaction_hash = $2 AND current AND payload_expected` fails due to: type/column mismatches (e.g. effective_gas_price string not castable to the target NUMERIC column), i64 out-of-range values for block_number/gas_used, CHECK/FK constraint violations, a lost DB connection mid-transaction, or concurrent DDL/schema drift.

Common situations: Migration evidence whose effective_gas_price does not parse into the target column; block numbers or gas values exceeding column range; migration run against a database whose schema predates a required column; connection timeouts during a long-running migration batch.

Related errors


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