{"record":{"id":"3d80d870f900f077","repo":"nautechsystems/nautilus_trader","slug":"verified-nonce-assignment-requires-the-configured","errorCode":null,"errorMessage":"Verified nonce assignment requires the configured failure domains","messagePattern":"Verified nonce assignment requires the configured failure domains","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":5900,"sourceCode":"    /// Assigns the canonical nonce and its authorizing verification evidence atomically.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the durable nonce ledger, intent ownership, manifest identity, or\n    /// evidence is inconsistent, or if persistence fails.\n    pub(crate) async fn assign_execution_intent_nonce_verified(\n        &self,\n        assignment: &ExecutionNonceAssignment<'_>,\n    ) -> anyhow::Result<()> {\n        anyhow::ensure!(\n            !assignment.decisions.is_empty(),\n            \"Verified nonce assignment requires decision evidence\"\n        );\n        anyhow::ensure!(\n            assignment.provider_ids.len() == 3 && assignment.operator_ids.len() == 3,\n            \"Verified nonce assignment requires exactly three provider and operator IDs\"\n        );\n        anyhow::ensure!(\n            assignment.failure_domain_ids.len() >= 3,\n            \"Verified nonce assignment requires the configured failure domains\"\n        );\n        let chain_id = i32::try_from(assignment.chain_id)\n            .context(\"Verification chain ID exceeds PostgreSQL INTEGER\")?;\n        let nonce =\n            i64::try_from(assignment.nonce).context(\"Execution nonce exceeds PostgreSQL BIGINT\")?;\n        let mut transaction = self\n            .pool\n            .begin()\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to start verified nonce assignment: {e}\"))?;\n        let (manifest_version, manifest_digest, next_nonce, revision) =\n            sqlx::query_as::<_, (String, String, i64, i64)>(\n                \"\n                SELECT manifest_version, manifest_digest, next_canonical_nonce, revision\n                FROM execution_verification_nonce\n                WHERE chain_id = $1 AND wallet_address = $2","sourceCodeStart":5882,"sourceCodeEnd":5918,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L5882-L5918","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Populate failure_domain_ids with at least three distinct failure domains before calling the verified assignment path.","Fix the failure-domain configuration/registry so at least three domains are registered and healthy.","Adjust provider/operator selection to spread across distinct failure domains rather than picking collocated nodes.","Verify topology metadata ingestion is working (no stale/missing failure-domain labels in the registry)."],"exampleFix":"// before: assignment built with whatever domains were handy\nlet assignment = ExecutionNonceAssignment { failure_domain_ids: domains, .. };\n// after\nanyhow::ensure!(domains.len() >= 3, \"need >=3 failure domains, got {}\", domains.len());\nlet assignment = ExecutionNonceAssignment { failure_domain_ids: domains, .. };\ndatabase.assign_execution_intent_nonce_verified(&assignment).await?;","handlingStrategy":"validation","validationCode":"// validate failure-domain spread before the verified call\nlet distinct: std::collections::HashSet<_> = assignment.failure_domain_ids.iter().collect();\nif distinct.len() < 3 {\n    return Err(anyhow::anyhow!(\"verified assignment needs >=3 failure domains, got {}\", distinct.len()));\n}","typeGuard":"fn spans_enough_failure_domains(a: &ExecutionNonceAssignment<'_>) -> bool {\n    a.failure_domain_ids.iter().collect::<std::collections::HashSet<_>>().len() >= 3\n}","tryCatchPattern":"match database.assign_execution_intent_nonce_verified(&assignment).await {\n    Err(e) if e.to_string().contains(\"configured failure domains\") => {\n        tracing::error!(domains = assignment.failure_domain_ids.len(), \"insufficient failure-domain spread\");\n        return Err(e);\n    }\n    Ok(()) => {},\n    Err(e) => return Err(e),\n}","preventionTips":["Register and health-check at least three failure domains before running verified assignments.","Spread provider/operator selection across distinct failure domains.","Verify topology metadata is fresh — stale labels collapse domains.","Alert when the failure-domain registry reports fewer than three healthy domains."],"tags":["validation","fault-tolerance","topology","configuration"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}