nautechsystems/nautilus_trader · error · anyhow::Error
Verified nonce assignment requires exactly three provider an
Error message
Verified nonce assignment requires exactly three provider and operator IDs
What it means
The verified nonce-assignment path enforces a BFT-style quorum: it requires exactly three provider IDs and exactly three operator IDs in the ExecutionNonceAssignment. The ensure! at database.rs:5896 rejects any other cardinality, because the verification logic is designed for a 3-of-N provider/operator configuration.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:5896
);
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()
.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)>(View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure exactly three provider IDs and three operator IDs are included in the assignment before calling the verified path.
- Fix the cluster/provider configuration so the deployment matches the required 3-provider/3-operator topology.
- If aggregation deduplicates or truncates, correct the aggregation logic to preserve exactly three distinct IDs.
- If a different quorum size is genuinely required, that is a code change to the invariant, not a caller workaround — coordinate with maintainers.
Example fix
// before: passing whatever providers responded
let assignment = ExecutionNonceAssignment { provider_ids: responded_providers, operator_ids: responded_operators, .. };
// after
anyhow::ensure!(responded_providers.len() == 3 && responded_operators.len() == 3,
"need 3 providers and 3 operators, got {}/{}", responded_providers.len(), responded_operators.len());
let assignment = ExecutionNonceAssignment { provider_ids: responded_providers, operator_ids: responded_operators, .. };
database.assign_execution_intent_nonce_verified(&assignment).await?; Defensive patterns
Strategy: validation
Validate before calling
// validate quorum cardinality before the verified call
if assignment.provider_ids.len() != 3 || assignment.operator_ids.len() != 3 {
return Err(anyhow::anyhow!(
"verified assignment needs exactly 3 providers/3 operators, got {}/{}",
assignment.provider_ids.len(), assignment.operator_ids.len()));
} Type guard
fn has_full_quorum(a: &ExecutionNonceAssignment<'_>) -> bool {
a.provider_ids.len() == 3 && a.operator_ids.len() == 3
} Try / catch
match database.assign_execution_intent_nonce_verified(&assignment).await {
Err(e) if e.to_string().contains("exactly three provider and operator IDs") => {
tracing::error!(providers = assignment.provider_ids.len(), operators = assignment.operator_ids.len(), "quorum cardinality mismatch");
return Err(e);
}
Ok(()) => {},
Err(e) => return Err(e),
} Prevention
- Keep deployment topology at exactly 3 providers and 3 operators for the verified path.
- Deduplicate IDs during aggregation and assert the count equals 3.
- Fail fast when fewer than 3 providers respond instead of degrading the assignment.
- Sync cluster-size config changes with the quorum expectation in code.
When it happens
Trigger: Passing an ExecutionNonceAssignment where provider_ids.len() != 3 or operator_ids.len() != 3 — e.g. built from a config with fewer/more providers, partially collected evidence (2 providers responded), or duplicated/missing IDs when aggregating decisions.
Common situations: Misconfigured cluster running with only 2 operators available; evidence aggregation dropping duplicates (leaving <3) or including extras; changing cluster size without updating the verified-assignment quorum expectation; environment mismatch between staging (smaller cluster) and production (3 operators).
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
- Connect verification evidence is incomplete
- Verified action requires the configured provider identities
- max_fee_per_contract is required
- max_fee_per_contract must be greater than zero
- Failed to create HTTP client: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/2997dbc8ad2d384a.
Report an issue: GitHub.