{"record":{"id":"7129f4c1c438872b","repo":"nautechsystems/nautilus_trader","slug":"a-signed-transaction-requires-exactly-one-payload","errorCode":null,"errorMessage":"A signed transaction requires exactly one payload representation","messagePattern":"A signed transaction requires exactly one payload representation","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":6609,"sourceCode":"        self.add_execution_transaction_payload(\n            intent_id,\n            chain_id,\n            transaction_hash,\n            None,\n            Some(sealed_transaction),\n        )\n        .await\n    }\n\n    async fn add_execution_transaction_payload(\n        &self,\n        intent_id: i64,\n        chain_id: u32,\n        transaction_hash: &str,\n        raw_transaction: Option<&[u8]>,\n        sealed_transaction: Option<&[u8]>,\n    ) -> anyhow::Result<ExecutionTransactionHashRow> {\n        anyhow::ensure!(\n            raw_transaction.is_some() != sealed_transaction.is_some(),\n            \"A signed transaction requires exactly one payload representation\"\n        );\n        let chain_id_db = i32::try_from(chain_id)\n            .with_context(|| format!(\"Chain ID {chain_id} exceeds PostgreSQL INTEGER\"))?;\n        let mut transaction =\n            self.pool.begin().await.map_err(|e| {\n                anyhow::anyhow!(\"Failed to start signed transaction persistence: {e}\")\n            })?;\n\n        if let Some(envelope) = sealed_transaction {\n            let state_row = sqlx::query(\n                \"SELECT deployment_id, protocol_version, operation, active_key_id \\\n                 FROM execution_payload_state WHERE component = 'signed_transactions' FOR SHARE\",\n            )\n            .fetch_optional(&mut *transaction)\n            .await\n            .context(\"failed to lock execution payload state for protected persistence\")?","sourceCodeStart":6591,"sourceCodeEnd":6627,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L6591-L6627","documentation":"`add_execution_transaction_payload` (backing `add_execution_transaction_hash` and `add_execution_transaction_envelope`) accepts either a raw (plaintext) signed transaction or a sealed (encrypted envelope) transaction, but not both and not neither. `anyhow::ensure!` fires this error when the exclusive-OR check `raw_transaction.is_some() != sealed_transaction.is_some()` fails.","triggerScenarios":"Calling `add_execution_transaction_payload` directly with both `raw_transaction` and `sealed_transaction` set to Some, or with both set to None. The public wrappers (`add_execution_transaction_hash`, `add_execution_transaction_envelope`) cannot trigger it; only misuse of the internal payload API can.","commonSituations":"A caller migrating code from the raw API to the envelope API passes both payloads 'just in case'; a refactor switches to None-for-both placeholder arguments; test scaffolding constructs the call with both fields populated.","solutions":["Pass exactly one of `raw_transaction` or `sealed_transaction` as Some and the other as None","Use the public wrappers `add_execution_transaction_hash` (raw) or `add_execution_transaction_envelope` (sealed) instead of calling `add_execution_transaction_payload` directly","Note: if payload protection has been activated in the database (execution_schema_version marker exists), raw persistence is rejected — use the sealed envelope path"],"exampleFix":"// before\ndb.add_execution_transaction_payload(intent_id, chain_id, &hash, Some(raw), Some(sealed)).await?;\n// after\ndb.add_execution_transaction_payload(intent_id, chain_id, &hash, None, Some(sealed)).await?;","handlingStrategy":"validation","validationCode":"fn validate_payload_args(raw: Option<&[u8]>, sealed: Option<&[u8]>) -> Result<(), &'static str> {\n    match (raw, sealed) {\n        (Some(_), None) | (None, Some(_)) => Ok(()),\n        _ => Err(\"pass exactly one of raw_transaction or sealed_transaction\"),\n    }\n}","typeGuard":null,"tryCatchPattern":"match validate_payload_args(raw, sealed) {\n    Err(msg) => { tracing::error!(\"{msg}\"); return Err(anyhow::anyhow!(msg)); }\n    Ok(()) => db.add_execution_transaction_payload(intent_id, chain_id, &hash, raw, sealed).await?,\n}","preventionTips":["Prefer the typed wrappers add_execution_transaction_hash / add_execution_transaction_envelope over the Option-pair API","Use enum Payload::Raw(&[u8]) | Payload::Sealed(&[u8]) at call sites so the compiler enforces exactly-one","After switching from raw to envelope persistence, remove legacy Some(raw) arguments rather than keeping both","Unit-test the call site with both payload variants"],"tags":["validation","api-misuse","mutually-exclusive","signed-transactions"],"backgroundTag":"mutually-exclusive-options","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"}