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
- 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.
- Ensure the caller does not replay the same decisions for an intent that already has them persisted; record a batch only once.
- Verify provider_ids/operator_ids/failure_domain_ids bind as the exact array types the migration defines.
- 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
- Record each pre-sign decision batch exactly once; make inserts idempotent on transition_key.
- Keep provider_ids/operator_ids/failure_domain_ids element types aligned with the migration's column types.
- Range-check heights and digest lengths before building the assignment.
- Keep database migrations in sync with the struct definitions.
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
- Failed to insert into pool_swap table: {e}
- Failed to insert into pool_liquidity table: {e}
- Failed to lock execution intent for nonce assignment: {e}
- Failed to insert into trader table: {e}
- Failed to insert into order table: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/236fc44270ac95e1.
Report an issue: GitHub.