nautechsystems/nautilus_trader · error

Failed to persist pre-sign verification: {e}

Error message

Failed to persist pre-sign verification: {e}

What it means

Inside the same transaction, each pre-sign verification decision is INSERTed into `execution_verification_decision`. This error wraps any SQLx failure of those INSERTs (constraint violation, type mismatch on bound columns, array binding failures, connection errors). The library throws it because the verified nonce assignment requires durable decision evidence; if evidence cannot be persisted, the nonce must not be assigned and the transaction aborts.

Source

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

                )
                ",
            )
            .bind(assignment.intent_id)
            .bind(nonce)
            .bind(decision.read_class)
            .bind(height_start)
            .bind(height_end)
            .bind(assignment.manifest_version)
            .bind(assignment.manifest_digest)
            .bind(assignment.provider_ids)
            .bind(assignment.operator_ids)
            .bind(assignment.failure_domain_ids)
            .bind(&decision.normalized_value_digest)
            .bind(revision)
            .bind(transition_key)
            .execute(&mut *transaction)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to persist pre-sign verification: {e}"))?;
        }

        let result = sqlx::query(
            "
            UPDATE execution_intent
            SET nonce = $2, updated_at = NOW()
            WHERE id = $1
              AND status = 'prepared'
              AND active
              AND (nonce IS NULL OR nonce = $2)
            ",
        )
        .bind(assignment.intent_id)
        .bind(nonce)
        .execute(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to assign verified execution nonce: {e}"))?;
        anyhow::ensure!(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the inner `{e}` for the concrete Postgres error: 23505 means duplicate evidence (make the operation idempotent with ON CONFLICT or skip already-recorded decisions); 22P02/42804 means a binding type mismatch.
  2. Ensure the caller does not replay the same decisions for an intent that already has them persisted; record a batch only once.
  3. Verify provider_ids/operator_ids/failure_domain_ids bind as the exact array types the migration defines.
  4. Check heights fit PostgreSQL BIGINT and digests match the column definition; apply pending migrations if the schema is behind.

Example fix

// before: always inserting, failing on replay
INSERT INTO execution_verification_decision (...) VALUES (...)
// after: idempotent insert for replays
INSERT INTO execution_verification_decision (...) VALUES (...)
ON CONFLICT (intent_id, transition_key) DO NOTHING;
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check that evidence has not already been recorded (avoids unique violations on replay)
let existing: i64 = sqlx::query_scalar("SELECT count(*) FROM execution_verification_decision WHERE intent_id = $1 AND transition_key LIKE 'pre_sign:%'")
    .bind(&assignment.intent_id).fetch_one(pool).await?;
if existing > 0 { /* decisions already persisted; skip re-insert */ }

Try / catch

if let Err(e) = db.assign_execution_intent_nonce_verified(&assignment).await {
    if e.to_string().contains("Failed to persist pre-sign verification") {
        if e.to_string().contains("23505") { /* duplicate evidence: treat as already recorded */ }
        else { return Err(e); }
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: A decision row violating a UNIQUE constraint (duplicate transition_key `pre_sign:{intent_id}:{read_class}:{index}` or (intent_id, nonce) key on re-assignment); provider_ids/operator_ids/failure_domain_ids array elements not matching column types; oversized digests or out-of-i64-range heights; connection loss during the loop.

Common situations: Retrying a verified assignment after a partial commit of decisions (unique violation on transition_key); callers passing evidence with different read_class values than a previous attempt; schema drift between the Rust struct and the migration; passing more than the allowed number of decisions for a single transition key space.

Related errors


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