nautechsystems/nautilus_trader · error · anyhow::Error

Verified nonce assignment requires the configured failure do

Error message

Verified nonce assignment requires the configured failure domains

What it means

The verified nonce-assignment path requires assignments spanning at least three failure domains, enforced by the ensure! at database.rs:5900. This guarantees the nonce decision is not concentrated in a single failure domain, preserving independent-failure assumptions for the quorum verification.

Source

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

    /// 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()
            .await
            .map_err(|e| anyhow::anyhow!("Failed to start verified nonce assignment: {e}"))?;
        let (manifest_version, manifest_digest, next_nonce, revision) =
            sqlx::query_as::<_, (String, String, i64, i64)>(
                "
                SELECT manifest_version, manifest_digest, next_canonical_nonce, revision
                FROM execution_verification_nonce
                WHERE chain_id = $1 AND wallet_address = $2

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Populate failure_domain_ids with at least three distinct failure domains before calling the verified assignment path.
  2. Fix the failure-domain configuration/registry so at least three domains are registered and healthy.
  3. Adjust provider/operator selection to spread across distinct failure domains rather than picking collocated nodes.
  4. Verify topology metadata ingestion is working (no stale/missing failure-domain labels in the registry).

Example fix

// before: assignment built with whatever domains were handy
let assignment = ExecutionNonceAssignment { failure_domain_ids: domains, .. };
// after
anyhow::ensure!(domains.len() >= 3, "need >=3 failure domains, got {}", domains.len());
let assignment = ExecutionNonceAssignment { failure_domain_ids: domains, .. };
database.assign_execution_intent_nonce_verified(&assignment).await?;
Defensive patterns

Strategy: validation

Validate before calling

// validate failure-domain spread before the verified call
let distinct: std::collections::HashSet<_> = assignment.failure_domain_ids.iter().collect();
if distinct.len() < 3 {
    return Err(anyhow::anyhow!("verified assignment needs >=3 failure domains, got {}", distinct.len()));
}

Type guard

fn spans_enough_failure_domains(a: &ExecutionNonceAssignment<'_>) -> bool {
    a.failure_domain_ids.iter().collect::<std::collections::HashSet<_>>().len() >= 3
}

Try / catch

match database.assign_execution_intent_nonce_verified(&assignment).await {
    Err(e) if e.to_string().contains("configured failure domains") => {
        tracing::error!(domains = assignment.failure_domain_ids.len(), "insufficient failure-domain spread");
        return Err(e);
    }
    Ok(()) => {},
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Passing an ExecutionNonceAssignment whose failure_domain_ids has fewer than 3 entries — typically when providers/operators were selected from overlapping failure domains, the failure-domain registry returned too few domains, or topology metadata was missing so IDs defaulted to one or two.

Common situations: Deployments where the failure-domain configuration (e.g. regions/zones) is not fully populated; providers clustered in one region/zone; a topology service outage returning incomplete failure-domain metadata; misclassification of operator topology in the node registry.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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