nautechsystems/nautilus_trader · error · anyhow::Error
Failed to persist migration evidence: {e}
Error message
Failed to persist migration evidence: {e} What it means
This error wraps any sqlx failure that occurs while inserting a row into the `execution_verification_decision` table during migration of prior execution records. The migration routine replays each recorded decision (read class, height range, normalized value digest, etc.) as an 'migration'-classified evidence row inside a transaction; if that INSERT fails — schema mismatch, constraint violation, type conversion, or connection loss — the underlying sqlx error is wrapped via anyhow::anyhow! with this message and the whole migration transaction aborts.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:4362
)
",
)
.bind(record.intent_id)
.bind(nonce)
.bind(decision.read_class)
.bind(height_start)
.bind(height_end)
.bind(bootstrap.manifest_version)
.bind(bootstrap.manifest_digest)
.bind(bootstrap.provider_ids)
.bind(bootstrap.operator_ids)
.bind(bootstrap.failure_domain_ids)
.bind(&decision.normalized_value_digest)
.bind(format!("migration:{}:{index}", record.intent_id))
.execute(&mut *transaction)
.await
.map_err(|e| {
anyhow::anyhow!("Failed to persist migration evidence: {e}")
})?;
}
}
}
0
};
sqlx::query(
"
INSERT INTO execution_verified_finalized_header (
chain_id, wallet_address, number, hash, parent_hash, timestamp,
base_fee_per_gas, manifest_digest
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT (chain_id, wallet_address, number) DO NOTHING
",
)
.bind(chain_id)View on GitHub (pinned to 18893faf8b)
Solutions
- Run the latest schema migrations for this crate so `execution_verification_decision` matches the columns the INSERT expects (intent_id, nonce, decision_class, read_class, height_start, height_end, manifest_version, manifest_digest, provider_ids, operator_ids, failure_domain_ids, response_class, normalized_value_digest, nonce_revision, outcome, transition_key).
- Inspect the wrapped sqlx error after the colon — it names the exact column/constraint that failed (e.g. duplicate transition_key, null value, FK violation on intent_id).
- Verify the intent record was inserted successfully in the same transaction before its decisions; fix ordering or data in the migration source record.
- Ensure the decision's height_start/height_end and nonce fit in i64 (u64 values above i64::MAX must be rejected before migration).
- Check database connectivity, pool limits, and statement timeouts; retry the migration after the transient issue is resolved — the transaction rolls back atomically.
Example fix
// before
.execute(&mut *transaction)
.await
.map_err(|e| {
anyhow::anyhow!("Failed to persist migration evidence: {e}")
})?;
// after — surface constraint details and context for diagnosis
.execute(&mut *transaction)
.await
.map_err(|e| {
anyhow::anyhow!(
"Failed to persist migration evidence for intent {} decision {index}: {e:#}",
record.intent_id
)
})?; Defensive patterns
Strategy: validation
Validate before calling
// Pre-check before migrating a record's decisions
if let Some(nonce) = record.nonce {
anyhow::ensure!(nonce <= i64::MAX as u64, "nonce exceeds BIGINT");
}
for (i, d) in record.decisions.iter().enumerate() {
if let Some(h) = d.height_start { anyhow::ensure!(h <= i64::MAX as u64, "decision {i} height_start exceeds BIGINT"); }
if let Some(h) = d.height_end { anyhow::ensure!(h <= i64::MAX as u64, "decision {i} height_end exceeds BIGINT"); }
}
// Confirm the table exists with expected shape:
// SELECT 1 FROM information_schema.columns
// WHERE table_name='execution_verification_decision' AND column_name='normalized_value_digest'; Try / catch
match migration_result {
Err(e) if e.to_string().contains("duplicate key") => warn!("decision already migrated; continuing idempotently"),
Err(e) => return Err(anyhow!("migration aborted: {e:#}")),
Ok(v) => info!("migrated {v} records"),
} Prevention
- Keep schema migrations applied in the same deploy step as the adapter binary upgrade
- Pre-validate u64 heights/nonces fit i64 before starting the migration transaction
- Ensure intents are inserted before their decision rows in the same transaction
- Monitor DB connectivity and pool saturation during bulk migrations
When it happens
Trigger: Calling the migration/bootstrap path that persists `record.decisions` when: the `execution_verification_decision` table is missing or has a different schema (stale migration state), a bound value violates a NOT NULL/UNIQUE/FK constraint (e.g. intent_id not yet inserted, null normalized_value_digest), height values fall outside i64 range, or the PostgreSQL connection drops mid-transaction.
Common situations: Running the blockchain cache adapter against a database whose schema was created by an older version of the crate; migrating a record with decision rows referencing an intent that failed its own INSERT earlier in the transaction; DB connectivity blips or connection-pool exhaustion during long migrations; misconfigured `database_url` pointing at the wrong database.
Related errors
- Failed to lock migrated intent: {e}
- Failed to reconstruct migrated hash: {e}
- Failed to reconstruct migrated intent: {e}
- Failed to seed chain table: {e}
- Failed to call create_block_partition for chain {}: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/e3a8f2fc5e946b9c.
Report an issue: GitHub.