nautechsystems/nautilus_trader · error · anyhow::Error
Legacy execution transaction {} has no plaintext
Error message
Legacy execution transaction {} has no plaintext What it means
In the legacy load path, a row has neither a sealed envelope (expected there) nor a plaintext raw transaction, so there is nothing to return. The loader converts the missing raw_transaction into this anyhow error naming the row id.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:5252
)
})?;
let key_id = envelope_key_id(envelope)?;
anyhow::ensure!(
keys.contains_key(&key_id),
"Execution transaction {} requires an unavailable payload key",
hash.id
);
key_ids.insert(alloy::hex::encode(key_id));
let context = payload_context(&intent, hash, deployment_id)?;
keys.unseal(envelope, &context)?
} else {
anyhow::ensure!(
hash.sealed_transaction.is_none(),
"Legacy execution transaction {} contains an envelope",
hash.id
);
hash.raw_transaction.clone().ok_or_else(|| {
anyhow::anyhow!("Legacy execution transaction {} has no plaintext", hash.id)
})?
};
authenticate_retained_payload(
&raw_transaction,
&intent,
hash,
deployment_id.as_deref().unwrap_or(""),
)?;
if let Some(policy) = policy
&& retained_payload_requires_policy(&intent, hash, policy)?
{
authenticate_payload(
&raw_transaction,
&intent,
hash,
policy,
deployment_id.as_deref().unwrap_or(""),View on GitHub (pinned to 18893faf8b)
Solutions
- Restore the row's payload from a backup or re-derive it from the execution intent source.
- If the row was migrated to sealed form, load with keys and deployment_id so the protected path is used.
- Inspect the row: if sealed_transaction exists, switch to protected loading instead of expecting plaintext.
- Check writer logs for a failed payload write when the row was created.
Example fix
// Row missing payload in legacy mode
{ id: "0x123", raw_transaction: null, sealed_transaction: null }
// after restore
{ id: "0x123", raw_transaction: "0x02f8...", sealed_transaction: null } Defensive patterns
Strategy: validation
Validate before calling
-- Rows with no payload at all SELECT id FROM execution_transactions WHERE raw_transaction IS NULL AND sealed_transaction IS NULL;
Try / catch
let raw = match row.raw_transaction {
Some(bytes) => bytes,
None => return Err(anyhow!("row {} has no plaintext; restore or switch to protected load", row.id)),
}; Prevention
- Never NULL payload columns without writing the replacement form in the same transaction.
- Validate row completeness after any restore or migration.
- Monitor row counts where both payload columns are NULL.
When it happens
Trigger: Loading execution transactions without keys/deployment_id while a row's raw_transaction column is NULL (and sealed_transaction is also NULL, otherwise 2224 fires first).
Common situations: Rows whose payload was cleared after reclassification but not restored; a partially completed sealing migration deleted plaintext without writing envelopes; loading a truncated/partial backup.
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
- Protected execution transaction {} has no envelope
- Implement FromRow for FuturesSpread
- Implement FromRow for OptionSpread
- Execution schema version {} is newer than supported version
- Verified finalized transaction count advanced without an act
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/6c774240e9160cc1.
Report an issue: GitHub.