nautechsystems/nautilus_trader · error · anyhow::Error

Verified nonce assignment requires decision evidence

Error message

Verified nonce assignment requires decision evidence

What it means

assign_execution_intent_nonce_verified performs a cryptographically/independently verified nonce assignment and refuses to persist anything without decision evidence. The anyhow::ensure! at database.rs:5892 rejects an ExecutionNonceAssignment whose decisions slice is empty. Evidence is required so the assignment can be audited and validated against the quorum of decisions.

Source

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

        .map_err(|e| anyhow::anyhow!("Failed to assign nonce {nonce} to execution intent: {e}"))?;
        anyhow::ensure!(
            result.rows_affected() == 1,
            "Execution intent {intent_id} is not prepared for nonce {nonce}"
        );
        Ok(())
    }

    /// Assigns the canonical nonce and its authorizing verification evidence atomically.
    ///
    /// # Errors
    ///
    /// Returns an error if the durable nonce ledger, intent ownership, manifest identity, or
    /// evidence is inconsistent, or if persistence fails.
    pub(crate) async fn assign_execution_intent_nonce_verified(
        &self,
        assignment: &ExecutionNonceAssignment<'_>,
    ) -> anyhow::Result<()> {
        anyhow::ensure!(
            !assignment.decisions.is_empty(),
            "Verified nonce assignment requires decision evidence"
        );
        anyhow::ensure!(
            assignment.provider_ids.len() == 3 && assignment.operator_ids.len() == 3,
            "Verified nonce assignment requires exactly three provider and operator IDs"
        );
        anyhow::ensure!(
            assignment.failure_domain_ids.len() >= 3,
            "Verified nonce assignment requires the configured failure domains"
        );
        let chain_id = i32::try_from(assignment.chain_id)
            .context("Verification chain ID exceeds PostgreSQL INTEGER")?;
        let nonce =
            i64::try_from(assignment.nonce).context("Execution nonce exceeds PostgreSQL BIGINT")?;
        let mut transaction = self
            .pool
            .begin()

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Populate assignment.decisions with the collected decision evidence before calling the verified assignment path.
  2. Fail fast upstream: check decisions.is_empty() (or the evidence collector's result) before constructing the assignment.
  3. If evidence is genuinely unavailable, use the non-verified assignment path only if policy allows, or abort the nonce assignment.
  4. Log/inspect why the evidence collector produced zero decisions (provider connectivity, wrong chain_id, misconfigured evidence source).

Example fix

// before
let assignment = ExecutionNonceAssignment { decisions: Vec::new(), .. };
database.assign_execution_intent_nonce_verified(&assignment).await?;
// after
anyhow::ensure!(!decisions.is_empty(), "no decision evidence collected for nonce assignment");
let assignment = ExecutionNonceAssignment { decisions, .. };
database.assign_execution_intent_nonce_verified(&assignment).await?;
Defensive patterns

Strategy: validation

Validate before calling

// validate the assignment struct before the verified call
if assignment.decisions.is_empty() {
    return Err(anyhow::anyhow!("refusing verified nonce assignment: no decision evidence"));
}

Type guard

fn has_decision_evidence(a: &ExecutionNonceAssignment<'_>) -> bool {
    !a.decisions.is_empty()
}

Try / catch

match database.assign_execution_intent_nonce_verified(&assignment).await {
    Err(e) if e.to_string().contains("requires decision evidence") => {
        // fall back to re-collecting evidence or abort the round
        recollect_evidence_and_retry().await?;
    }
    Ok(()) => {},
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Constructing or deserializing an ExecutionNonceAssignment with an empty decisions vector (e.g. all providers failed to return decisions, evidence collection was skipped, or the struct was built by hand in tests/tooling) and passing it to assign_execution_intent_nonce_verified.

Common situations: Upstream evidence collection silently returning zero records instead of failing; a provider outage leading to an empty decision list being passed through; test harnesses constructing minimal assignments without evidence; a refactor changing the decisions field to a default-empty collection.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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