nautechsystems/nautilus_trader · error · anyhow::Error

Failed to persist signed transaction {transaction_hash}: {e}

Error message

Failed to persist signed transaction {transaction_hash}: {e}

What it means

The INSERT ... ON CONFLICT statement that persists the signed transaction row failed with a database error. The library wraps the sqlx error with the transaction hash so the operator knows which signed payload failed to persist. This aborts the enclosing transaction, so nothing (including the intent status update) is committed.

Source

Thrown at crates/adapters/blockchain/src/cache/database.rs:6706

                      AND execution_transaction_hash.sealed_transaction IS NOT NULL
                  )
              )
            RETURNING
                id, intent_id, chain_id, transaction_hash, payload_expected,
                raw_transaction, sealed_transaction, status,
                block_number, block_hash, receipt_success, gas_used,
                effective_gas_price, current
            ",
        )
        .bind(intent_id)
        .bind(chain_id_db)
        .bind(transaction_hash)
        .bind(raw_transaction)
        .bind(sealed_transaction)
        .fetch_optional(&mut *transaction)
        .await
        .map_err(|e| {
            anyhow::anyhow!("Failed to persist signed transaction {transaction_hash}: {e}")
        })?
        .ok_or_else(|| {
            anyhow::anyhow!(
                "Signed transaction {transaction_hash} conflicts with its persisted identity"
            )
        })?;

        sqlx::query(
            "UPDATE execution_intent SET status = 'signed', updated_at = NOW() WHERE id = $1",
        )
        .bind(intent_id)
        .execute(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to mark execution intent signed: {e}"))?;
        sqlx::query(
            "
            INSERT INTO execution_transaction_transition (
                intent_id, transaction_hash_id, transition_key, from_status, to_status

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the inner sqlx error (the {e} in the message) — it names the precise SQL failure; fix that root cause.
  2. Retry with backoff for transient errors (connection, deadlock, serialization) — the operation is transactional and safe to redo.
  3. Ensure the intent has a nonce set before persisting, and that chain_id fits PostgreSQL INTEGER.
  4. Check pg_stat_activity for blocking transactions if timeouts/deadlocks recur.
Defensive patterns

Strategy: retry

Validate before calling

// ensure intent has a nonce (required by the INSERT's WHERE clause) and chain_id fits i32
let nonce: Option<i64> = sqlx::query_scalar("SELECT nonce FROM execution_intent WHERE id=$1").bind(intent_id).fetch_one(&pool).await?;
anyhow::ensure!(nonce.is_some(), "intent must have a nonce before persisting a signed transaction");

Try / catch

let row = retry_if(3, || db.add_execution_transaction(...), |e| {
    let s = e.to_string();
    s.contains("Failed to persist signed transaction") && is_transient(&s)
}).await?;

Prevention

When it happens

Trigger: The INSERT into execution_transaction_hash errored: connection drop, statement timeout, a SQL-level failure such as type/integer overflow on chain_id or serialization failure, or deadlock with a concurrent writer. Note: zero rows from the INSERT's SELECT WHERE clause (intent missing nonce) is the distinct conflict error, not this one.

Common situations: Network interruption to PostgreSQL mid-transaction; concurrent signing workers deadlocking on the same chain/hash rows; oversized raw_transaction bytea exceeding field/server limits; database under load causing timeouts.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/ad1a8b67c55eea5f. Report an issue: GitHub.