nautechsystems/nautilus_trader · error
A signed transaction requires exactly one payload representa
Error message
A signed transaction requires exactly one payload representation
What it means
`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.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:6609
self.add_execution_transaction_payload(
intent_id,
chain_id,
transaction_hash,
None,
Some(sealed_transaction),
)
.await
}
async fn add_execution_transaction_payload(
&self,
intent_id: i64,
chain_id: u32,
transaction_hash: &str,
raw_transaction: Option<&[u8]>,
sealed_transaction: Option<&[u8]>,
) -> anyhow::Result<ExecutionTransactionHashRow> {
anyhow::ensure!(
raw_transaction.is_some() != sealed_transaction.is_some(),
"A signed transaction requires exactly one payload representation"
);
let chain_id_db = i32::try_from(chain_id)
.with_context(|| format!("Chain ID {chain_id} exceeds PostgreSQL INTEGER"))?;
let mut transaction =
self.pool.begin().await.map_err(|e| {
anyhow::anyhow!("Failed to start signed transaction persistence: {e}")
})?;
if let Some(envelope) = sealed_transaction {
let state_row = sqlx::query(
"SELECT deployment_id, protocol_version, operation, active_key_id \
FROM execution_payload_state WHERE component = 'signed_transactions' FOR SHARE",
)
.fetch_optional(&mut *transaction)
.await
.context("failed to lock execution payload state for protected persistence")?View on GitHub (pinned to 18893faf8b)
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
Example fix
// before db.add_execution_transaction_payload(intent_id, chain_id, &hash, Some(raw), Some(sealed)).await?; // after db.add_execution_transaction_payload(intent_id, chain_id, &hash, None, Some(sealed)).await?;
Defensive patterns
Strategy: validation
Validate before calling
fn validate_payload_args(raw: Option<&[u8]>, sealed: Option<&[u8]>) -> Result<(), &'static str> {
match (raw, sealed) {
(Some(_), None) | (None, Some(_)) => Ok(()),
_ => Err("pass exactly one of raw_transaction or sealed_transaction"),
}
} Try / catch
match validate_payload_args(raw, sealed) {
Err(msg) => { tracing::error!("{msg}"); return Err(anyhow::anyhow!(msg)); }
Ok(()) => db.add_execution_transaction_payload(intent_id, chain_id, &hash, raw, sealed).await?,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Exactly one of client_order_id or venue_order_id is required
- Unsupported `OrderSide` for Binance: {value:?}
- invalid OrderSide: must be Buy or Sell, was {side}
- Chart function must be callable, was {type(f)}
- Chart function must be callable, was {type(func)}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7129f4c1c438872b.
Report an issue: GitHub.