nautechsystems/nautilus_trader · error
Execution transaction {} has no signed payload
Error message
Execution transaction {} has no signed payload What it means
When preparing a transaction for submission, the code checks that the hash row has `payload_expected = true`, meaning a signed transaction payload must exist. If the flag is set but no signed payload was actually produced/attached, the transaction cannot be encoded and submission aborts. This is a pipeline consistency check between payload production and hash bookkeeping.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:3619
let mut current = hashes.iter().filter(|row| row.current);
let row = current
.next()
.ok_or_else(|| anyhow::anyhow!("Execution intent {intent_id} has no current hash"))?;
anyhow::ensure!(
current.next().is_none(),
"Execution intent {intent_id} has more than one current hash"
);
Ok(row)
}
fn open_execution_payload(
keys: &PayloadKeySet,
policy: PayloadPolicy,
intent: &ExecutionIntentRow,
hash: &ExecutionTransactionHashRow,
reason: &str,
) -> anyhow::Result<Vec<u8>> {
anyhow::ensure!(
hash.payload_expected,
"Execution transaction {} has no signed payload",
hash.transaction_hash
);
anyhow::ensure!(
hash.raw_transaction.is_none(),
"Protected execution transaction {} contains plaintext",
hash.transaction_hash
);
let envelope = hash.sealed_transaction.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"Protected execution transaction {} has no sealed payload",
hash.transaction_hash
)
})?;
let context = payload_context(intent, hash, keys.deployment_id())?;
let raw_transaction = keys.unseal(envelope, &context)?;
log::info!(View on GitHub (pinned to 18893faf8b)
Solutions
- Re-run the signing step for this transaction hash so a signed payload is produced and persisted before submission.
- If the transaction legitimately has no signed payload, correct the row's payload_expected flag (or the upstream logic setting it) to match reality.
- Check signer/keystore configuration and credentials, since signer failure is the usual root cause of the missing payload.
- Replay the pipeline from the intent stage to regenerate the row and payload together.
Example fix
// before: row written before signing completed store.record_hash(row_with_payload_expected_true)?; // after: persist the signed payload first, or set the flag only when it exists let signed = signer.sign(unsigned_tx)?; row.raw_or_sealed_payload = Some(signed); row.payload_expected = true; store.record_hash(row)?;
Defensive patterns
Strategy: validation
Validate before calling
if hash.payload_expected && hash.raw_transaction.is_none() && hash.sealed_transaction.is_none() {
// re-sign or fail before invoking payload resolution
anyhow::bail!("tx {} missing signed payload; re-run signing", hash.transaction_hash);
} Type guard
fn has_payload(hash: &ExecutionTransactionHashRow) -> bool {
!hash.payload_expected || hash.raw_transaction.is_some() || hash.sealed_transaction.is_some()
} Try / catch
match resolve_payload(keys, policy, intent, hash, reason) {
Ok(bytes) => submit(bytes),
Err(e) if e.to_string().contains("no signed payload") => retry_after_resign(intent, hash),
Err(e) => return Err(e),
} Prevention
- Persist the signed payload and the hash row in one transaction so they can't diverge.
- Health-check the signer service before recording intents.
- Reconcile rows where payload_expected is true but payload columns are NULL.
When it happens
Trigger: Calling the payload-resolution function with a hash row whose `payload_expected` is true but whose signed payload (raw/envelope data) was never persisted — e.g. the signing step failed silently or the row was written before the payload arrived.
Common situations: Signer service down or unreachable when the intent was recorded; a crash between writing the hash row and persisting the signed payload; stale rows from a partially completed run; misconfigured payload policy that skips signing while the row still claims a payload is expected.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Outcome side token '{fee_token}' carried a non-zero fee {fee
- No order, orderTrigger, or orderPriorExecution data in event
- Betting account balance would become negative: {} {} ({})
- Intervals are not disjoint after resetting file names
- instrument metadata ID {} does not match {instrument_id}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/558acdfc49e2e280.
Report an issue: GitHub.