nautechsystems/nautilus_trader · error · anyhow::Error
Failed to record verified finality transition: {e}
Error message
Failed to record verified finality transition: {e} What it means
This error wraps any SQLx failure that occurs while inserting a row into the execution_transaction_transition table inside the verified-finality transaction (database.rs:7174). The library throws it because the transition audit row must be recorded atomically with the intent/nonce updates; if the INSERT fails the whole finality transition is rolled back and the caller receives an anyhow error containing the underlying database message.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:7174
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(finality.intent_id)
.bind(finality.transaction_hash)
.bind(transition_key)
.bind(current_status)
.bind(finality.status.as_str())
.bind(block_number)
.bind(finality.block_hash)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to record verified finality transition: {e}"))?;
let nonce_result = sqlx::query(
"
UPDATE execution_verification_nonce
SET next_canonical_nonce = $3, revision = revision + 1, updated_at = NOW()
WHERE chain_id = $1 AND wallet_address = $2
AND next_canonical_nonce = $4 AND revision = $5
",
)
.bind(chain_id)
.bind(finality.wallet_address)
.bind(next_nonce)
.bind(nonce)
.bind(revision)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to advance canonical nonce ledger: {e}"))?;
anyhow::ensure!(View on GitHub (pinned to 18893faf8b)
Solutions
- Read the wrapped {e} message to identify the root cause (connection error, constraint violation, or undefined table/column).
- If it is a unique violation on transition_key, verify the finality event is not being replayed; the transition may already be recorded — check execution_transaction_transition before re-submitting.
- If it is a connection/pool error, verify DATABASE_URL connectivity, pool size, and statement timeouts, then retry the whole finality transition (it is transactional and rolls back atomically).
- Run pending migrations so execution_transaction_transition exists with the schema this code expects.
- Validate the finality payload (block_number within i32/i64 column range, hash encodings) before calling the finality API.
- Check Postgres logs for the exact SQLSTATE to distinguish serialization/constraint failures from connectivity failures.
Example fix
// before: retrying single statements independently loses atomicity
match db.record_finality(&finality).await { Err(e) => log::error!("{e}"), ... }
// after: verify the transition is not already recorded, then retry atomically
if db.transition_exists(finality.intent_id, &transition_key).await? { return Ok(()); }
db.record_verified_finality(&finality).await.context("finality transition retry")?; Defensive patterns
Strategy: try-catch
Validate before calling
let exists: bool = sqlx::query_scalar(
"SELECT EXISTS(SELECT 1 FROM execution_transaction_transition WHERE transition_key = $1)")
.bind(&transition_key).fetch_one(&pool).await?;
if exists { return Ok(()); } // idempotent guard before retrying the transition Type guard
fn is_constraint_violation(e: &anyhow::Error) -> bool {
e.chain().any(|c| c.to_string().contains("duplicate key") || c.to_string().contains("foreign key"))
} Try / catch
match db.record_verified_finality(&finality).await {
Err(e) if is_constraint_violation(&e) => warn!("transition already recorded"),
Err(e) if is_transient(&e) => retry_with_backoff(|| db.record_verified_finality(&finality)),
Err(e) => return Err(e),
Ok(()) => {}
} Prevention
- Keep database migrations in lockstep with the adapter version before deploying.
- Make finality processing idempotent by checking the transition_key first.
- Monitor pool health and statement timeouts; alert on connection resets.
- Validate block_number and hash encodings against column types before submitting.
When it happens
Trigger: The INSERT INTO execution_transaction_transition fails: the PostgreSQL connection drops or the statement times out mid-transaction, a constraint is violated (e.g. a foreign key on intent_id/transaction_hash_id or a unique transition_key conflict from re-processing the same finality), or the column types of from_status/to_status/block_number/block_hash do not accept the bound values.
Common situations: Database migrations drift behind the deployed code so the transition table or a column is missing; duplicate finality callbacks are replayed for the same (status, tx hash, block) causing a unique-key violation on transition_key; transient connection resets or pool exhaustion under load; a malformed transaction_hash/block_hash whose encoding does not match the column type.
Related errors
- Failed to update {event_family} pool event-family checkpoint
- Failed to finalize pool event sync progress: {e}
- Failed to lock execution intent for nonce assignment: {e}
- Failed to persist pre-sign verification: {e}
- Failed to commit verified action evidence: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/c31128350a52f2ee.
Report an issue: GitHub.