nautechsystems/nautilus_trader · error · anyhow::Error
Failed to extend finalized header ledger: {e}
Error message
Failed to extend finalized header ledger: {e} What it means
This error wraps a sqlx/PostgreSQL failure that occurs while inserting a subsequent verified finalized header (all headers after the first) into `execution_verified_finalized_header` during ledger extension. The insert uses `ON CONFLICT (chain_id, wallet_address, number) DO NOTHING`, so it fails only on genuine database-level errors, not on duplicate rows. The original driver error is preserved in the message via `{e}` and the operation runs inside a transaction, so any failure rolls back the whole bootstrap.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:4484
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)
.bind(bootstrap.wallet_address)
.bind(number)
.bind(&header.hash)
.bind(&header.parent_hash)
.bind(timestamp)
.bind(&base_fee)
.bind(bootstrap.manifest_digest)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to extend finalized header ledger: {e}"))?;
let stored = sqlx::query_as::<_, (String, String, i64, Option<String>, String)>(
"
SELECT hash, parent_hash, timestamp, base_fee_per_gas, manifest_digest
FROM execution_verified_finalized_header
WHERE chain_id = $1 AND wallet_address = $2 AND number = $3
",
)
.bind(chain_id)
.bind(bootstrap.wallet_address)
.bind(number)
.fetch_one(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to validate finalized header ledger: {e}"))?;
anyhow::ensure!(
stored
== (
header.hash.clone(),
header.parent_hash.clone(),View on GitHub (pinned to 18893faf8b)
Solutions
- Read the wrapped `{e}` cause to identify the concrete sqlx/Postgres error (connection, constraint, timeout) before changing anything.
- Check database connectivity, pool limits, and statement/idle-in-transaction timeouts; retry the bootstrap on transient connection errors.
- Compare the running schema against the expected `execution_verified_finalized_header` definition and apply pending migrations after version upgrades.
- Reduce batch size or chunk the insert if statement_timeout is hit on large finalized ranges.
- Inspect PostgreSQL server logs at failure time for locks, deadlocks, or failover events; ensure a single writer owns the bootstrap transaction.
Example fix
// before: one huge transaction for the entire finalized range
let mut tx = pool.begin().await?;
for h in headers { insert_header(&mut tx, h).await?; }
// after: bounded chunks with retry on transient errors
for chunk in headers.chunks(500) {
let mut tx = pool.begin().await?;
for h in chunk { insert_header(&mut tx, h).await?; }
tx.commit().await?; // smaller statements, shorter lock hold
} Defensive patterns
Strategy: retry
Validate before calling
let reachable = sqlx::query("SELECT 1").execute(&pool).await.is_ok();
if !reachable { return Err(anyhow!("database unavailable before bootstrap")); }
let schema_ok = sqlx::query(
"SELECT 1 FROM information_schema.tables WHERE table_name = 'execution_verified_finalized_header'",
).fetch_optional(&pool).await?.is_some();
if !schema_ok { return Err(anyhow!("ledger table missing; run migrations")); } Try / catch
match insert_finalized_headers(&mut tx, &headers).await {
Err(e) if is_transient(&e) => backoff_retry(|| insert_finalized_headers(&mut tx, &headers), 3).await?,
Err(e) if e.to_string().contains("Failed to extend finalized header ledger") => {
tracing::error!(cause = %e, "ledger extension failed");
return Err(e);
}
Ok(()) => {}
} Prevention
- Run migrations as a deployment step before starting the adapter.
- Size connection pools and statement timeouts for the largest expected finalized batch.
- Chunk large inserts to keep transactions short.
- Monitor PostgreSQL failover/lock events during bootstrap windows.
When it happens
Trigger: Any PostgreSQL error during the INSERT for headers 2..n of `bootstrap.finalized_headers`: connection dropped mid-transaction, constraint violation other than the ignored unique conflict (e.g. NOT NULL or type errors from oversized/malformed hash or base_fee strings), lock timeout on the ledger rows, statement timeout, or the table/sequence being unavailable or migrated concurrently.
Common situations: Network blips between the adapter and PostgreSQL during long bootstrap transactions; schema drift after an upgrade (columns/added NOT NULL constraints); exceeding statement_timeout on very large finalized batches; database failover; connection pool exhaustion under concurrent bootstraps.
Related errors
- Failed to seed chain table: {e}
- Failed to call create_block_partition for chain {}: {e}
- Failed to call create_token_partition for chain {}: {e}
- Failed to get block info for chain {}: {}
- Failed to insert into block table: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/dc14126daab1c692.
Report an issue: GitHub.