{"record":{"id":"c0097569b0cccbe3","repo":"nautechsystems/nautilus_trader","slug":"verified-nonce-assignment-requires-decision-eviden","errorCode":null,"errorMessage":"Verified nonce assignment requires decision evidence","messagePattern":"Verified nonce assignment requires decision evidence","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":5892,"sourceCode":"        .map_err(|e| anyhow::anyhow!(\"Failed to assign nonce {nonce} to execution intent: {e}\"))?;\n        anyhow::ensure!(\n            result.rows_affected() == 1,\n            \"Execution intent {intent_id} is not prepared for nonce {nonce}\"\n        );\n        Ok(())\n    }\n\n    /// 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()","sourceCodeStart":5874,"sourceCodeEnd":5910,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L5874-L5910","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Populate assignment.decisions with the collected decision evidence before calling the verified assignment path.","Fail fast upstream: check decisions.is_empty() (or the evidence collector's result) before constructing the assignment.","If evidence is genuinely unavailable, use the non-verified assignment path only if policy allows, or abort the nonce assignment.","Log/inspect why the evidence collector produced zero decisions (provider connectivity, wrong chain_id, misconfigured evidence source)."],"exampleFix":"// before\nlet assignment = ExecutionNonceAssignment { decisions: Vec::new(), .. };\ndatabase.assign_execution_intent_nonce_verified(&assignment).await?;\n// after\nanyhow::ensure!(!decisions.is_empty(), \"no decision evidence collected for nonce assignment\");\nlet assignment = ExecutionNonceAssignment { decisions, .. };\ndatabase.assign_execution_intent_nonce_verified(&assignment).await?;","handlingStrategy":"validation","validationCode":"// validate the assignment struct before the verified call\nif assignment.decisions.is_empty() {\n    return Err(anyhow::anyhow!(\"refusing verified nonce assignment: no decision evidence\"));\n}","typeGuard":"fn has_decision_evidence(a: &ExecutionNonceAssignment<'_>) -> bool {\n    !a.decisions.is_empty()\n}","tryCatchPattern":"match database.assign_execution_intent_nonce_verified(&assignment).await {\n    Err(e) if e.to_string().contains(\"requires decision evidence\") => {\n        // fall back to re-collecting evidence or abort the round\n        recollect_evidence_and_retry().await?;\n    }\n    Ok(()) => {},\n    Err(e) => return Err(e),\n}","preventionTips":["Make evidence collection fail loudly instead of returning an empty list.","Never construct ExecutionNonceAssignment by hand without evidence in production paths.","Add an assertion/log when the decisions collector yields zero records.","Keep a unit test covering the empty-decisions rejection."],"tags":["validation","evidence","quorum","empty-input"],"backgroundTag":"empty-required-field","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"}