nautechsystems/nautilus_trader · error · anyhow::Error
Failed to start verified finality transition: {e}
Error message
Failed to start verified finality transition: {e} What it means
Thrown when `self.pool.begin()` fails, i.e. the database pool could not open a new SQL transaction to record a verified finality transition. The original sqlx error is wrapped with this context message so callers can tell which stage of finality persistence failed. No ledger rows are modified because the transaction never started.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:6931
.finalized_headers
.last()
.is_some_and(|header| header.number >= finality.block_number),
"Verified finality headers must form a continuous chain through the inclusion height"
);
let chain_id = i32::try_from(finality.chain_id)
.context("Verification chain ID exceeds PostgreSQL INTEGER")?;
let nonce =
i64::try_from(finality.nonce).context("Execution nonce exceeds PostgreSQL BIGINT")?;
let next_nonce = nonce
.checked_add(1)
.ok_or_else(|| anyhow::anyhow!("Canonical nonce overflow"))?;
let block_number = i64::try_from(finality.block_number)
.context("Finality block exceeds PostgreSQL BIGINT")?;
let gas_used = i64::try_from(finality.gas_used)
.context("Finality gas used exceeds PostgreSQL BIGINT")?;
let mut transaction =
self.pool.begin().await.map_err(|e| {
anyhow::anyhow!("Failed to start verified finality transition: {e}")
})?;
let (manifest_version, manifest_digest, stored_nonce, revision) =
sqlx::query_as::<_, (String, String, i64, i64)>(
"
SELECT manifest_version, manifest_digest, next_canonical_nonce, revision
FROM execution_verification_nonce
WHERE chain_id = $1 AND wallet_address = $2
FOR UPDATE
",
)
.bind(chain_id)
.bind(finality.wallet_address)
.fetch_optional(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to lock finality nonce ledger: {e}"))?
.ok_or_else(|| anyhow::anyhow!("Canonical nonce ledger is not initialized"))?;
anyhow::ensure!(
manifest_version == finality.manifest_versionView on GitHub (pinned to 18893faf8b)
Solutions
- Check database connectivity and pool health (DB reachable, credentials valid, listener up)
- Increase pool max_connections or acquire_timeout in sqlx pool config if the pool is exhausted under load
- Retry the finality recording with backoff — it is transactional and safe to re-attempt once connectivity is restored
- Inspect the wrapped sqlx error (`{e}` in the message) for the root cause (auth, TLS, timeout, connection refused)
Example fix
// before
// pool configured with defaults, starves under concurrent finality writes
PgPoolOptions::new().max_connections(5).connect(url).await
// after
PgPoolOptions::new()
.max_connections(20)
.acquire_timeout(Duration::from_secs(10))
.connect(url)
.await Defensive patterns
Strategy: retry
Validate before calling
// Rust: probe pool health before high-stakes finality writes
let healthy = sqlx::query("SELECT 1").execute(&db.pool).await.is_ok();
ensure!(healthy, "database pool unavailable; defer finality recording"); Try / catch
// Retry transactional start with bounded backoff
for attempt in 0..3 {
match db.record_execution_finality_verified(&finality).await {
Err(e) if e.to_string().contains("Failed to start verified finality transition")
&& attempt < 2 => tokio::time::sleep(backoff(attempt)).await,
other => { other?; break }
}
} Prevention
- Size the pool (max_connections) above peak concurrent finality writers and set a sane acquire_timeout
- Monitor DB connectivity and fail over before issuing finality writes
- Because the operation is transactional, safe retries are fine — retry idempotently with backoff
- Alert on the wrapped sqlx root cause (auth/TLS/refused) to distinguish config drift from transient outages
When it happens
Trigger: Calling record_execution_finality_verified (directly or via the execution pipeline) while the PostgreSQL pool is unavailable: pool exhausted (all connections checked out), database down/restarting, network partition to the DB, TLS/auth failure at connection time, or pool acquisition timeout.
Common situations: Under heavy load the pool's max_connections is exhausted by concurrent finality recordings; a failover or maintenance window dropped DB connectivity; connection lifetime settings closed idle connections while traffic resumed; misconfigured DATABASE_URL credentials after a rotation.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- Failed to start execution verification migration: {e}
- Failed to validate nonce recovery ownership: {e}
- Failed to commit signed transaction: {e}
- Failed to lock finality nonce ledger: {e}
- Failed to commit add_account transaction: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/c1ad970ae4df07ac.
Report an issue: GitHub.