nautechsystems/nautilus_trader · error · anyhow::Error
Failed to start signed transaction persistence: {e}
Error message
Failed to start signed transaction persistence: {e} What it means
In `add_execution_transaction_payload`, after validating the payload representation and chain ID, a database transaction is started via `self.pool.begin()`. This error wraps a failure of that `pool.begin().await` call, meaning no persistence work for the signed transaction was attempted — the transaction never started.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:6617
}
async fn add_execution_transaction_payload(
&self,
intent_id: i64,
chain_id: u32,
transaction_hash: &str,
raw_transaction: Option<&[u8]>,
sealed_transaction: Option<&[u8]>,
) -> anyhow::Result<ExecutionTransactionHashRow> {
anyhow::ensure!(
raw_transaction.is_some() != sealed_transaction.is_some(),
"A signed transaction requires exactly one payload representation"
);
let chain_id_db = i32::try_from(chain_id)
.with_context(|| format!("Chain ID {chain_id} exceeds PostgreSQL INTEGER"))?;
let mut transaction =
self.pool.begin().await.map_err(|e| {
anyhow::anyhow!("Failed to start signed transaction persistence: {e}")
})?;
if let Some(envelope) = sealed_transaction {
let state_row = sqlx::query(
"SELECT deployment_id, protocol_version, operation, active_key_id \
FROM execution_payload_state WHERE component = 'signed_transactions' FOR SHARE",
)
.fetch_optional(&mut *transaction)
.await
.context("failed to lock execution payload state for protected persistence")?
.ok_or_else(|| anyhow::anyhow!("Execution payload protection is not active"))?;
let state = execution_payload_state_from_row(&state_row)?;
anyhow::ensure!(
state.protocol_version == EXECUTION_PAYLOAD_PROTOCOL_VERSION
&& state.operation == "ready",
"Execution payload storage is not ready for protected persistence"
);
anyhow::ensure!(View on GitHub (pinned to 18893faf8b)
Solutions
- Verify database reachability and credentials (DATABASE_URL) and that Postgres is accepting connections
- Increase sqlx pool capacity/tune `acquire_timeout` if the pool is exhausted under broadcast load
- Retry the call — starting a transaction is safe to retry since nothing was persisted
- Check pool metrics/logs for connection acquisition timeouts and set max_lifetime below any intermediary TCP idle timeout
Example fix
// before
let mut transaction = self.pool.begin().await.map_err(|e| {
anyhow::anyhow!("Failed to start signed transaction persistence: {e}")
})?;
// after
let mut transaction = self.pool.begin().await.with_context(|| {
format!("Failed to start signed transaction persistence for intent {intent_id}: pool status: {}", self.pool.size())
})?; Defensive patterns
Strategy: retry
Validate before calling
// Verify pool connectivity before persisting
sqlx::query("SELECT 1").execute(&db.pool).await
.context("database pool unavailable for signed transaction persistence")?; Try / catch
let mut transaction = loop {
match db.pool.begin().await {
Ok(tx) => break tx,
Err(e) if attempts < 3 => { attempts += 1; tokio::time::sleep(backoff).await; }
Err(e) => return Err(anyhow::anyhow!("Failed to start signed transaction persistence: {e}")),
}
}; Prevention
- Keep the sqlx pool warm and sized for concurrent broadcast workers (min_connections, max_connections)
- Set acquire_timeout generously enough for burst load but below caller-level timeouts
- Set pool max_lifetime below any NAT/firewall idle timeout on the DB path
- Alert on pool acquisition wait times so exhaustion is caught before it errors
When it happens
Trigger: `self.pool.begin()` fails: the connection pool is exhausted (all connections checked out), the pool cannot acquire a healthy connection within `acquire_timeout`, the database is unreachable, credentials were rejected at connect time, or the database is starting up/shutting down.
Common situations: Postgres down or restarting when a signed transaction is persisted before broadcast; pool max_connections too small for concurrent execution workers; network partition or firewall dropping pooled idle connections; wrong DATABASE_URL after a config change.
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 assign nonce {nonce} to execution intent: {e}
- Failed to commit recoverable transition: {e}
- Failed to persist finality verification: {e}
- Failed to record verified finality receipt: {e}
- Failed to insert into position_event table: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/64103cd1a6f4cdb6.
Report an issue: GitHub.