nautechsystems/nautilus_trader · error · anyhow::Error

Failed to mark execution intent signed: {e}

Error message

Failed to mark execution intent signed: {e}

What it means

After the signed transaction row was persisted, the status advance (UPDATE execution_intent SET status = 'signed') failed with a database error. The library wraps the sqlx error; the enclosing transaction rolls back so the signed-transaction insert is not left committed without its status transition.

Source

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

        .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
            ) VALUES ($1, $2, $3, $4, 'signed')
            ON CONFLICT (intent_id, transition_key) DO NOTHING
            ",
        )
        .bind(intent_id)
        .bind(row.id)
        .bind(format!("signed:{transaction_hash}"))
        .bind(current_status)
        .execute(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to record signed transaction transition: {e}"))?;

        transaction
            .commit()

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Retry the whole add_execution_transaction call with backoff — it is idempotent and transactional, so a full retry is safe.
  2. Check the inner {e} to distinguish connectivity vs timeout vs constraint causes and address accordingly.
  3. Verify the pool points at the primary, not a read replica, and that statement_timeout is adequate.
  4. Reduce transaction duration to avoid mid-transaction failures.
Defensive patterns

Strategy: retry

Validate before calling

// verify connectivity/primary before long signing transactions
let role: String = sqlx::query_scalar("SELECT CASE WHEN pg_is_in_recovery() THEN 'replica' ELSE 'primary' END").fetch_one(&pool).await?;
anyhow::ensure!(role == "primary", "writes require the primary database");

Try / catch

retry(3, backoff(200).factor(2.0), || async {
    db.add_execution_transaction(intent_id, chain_id, hash, sealed).await
}).await?; // transactional + idempotent, safe to retry whole flow

Prevention

When it happens

Trigger: The UPDATE statement errored: connection loss, statement timeout, deadlock with another transaction touching the execution_intent row, or writes being routed to a read-only replica.

Common situations: DB failover or pool invalidation mid-transaction; very large signed payload slowing the transaction past statement_timeout; concurrent maintenance blocking the update.

Related errors


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