nautechsystems/nautilus_trader · critical · anyhow::Error
Execution payload {} changed during seal round trip
Error message
Execution payload {} changed during seal round trip What it means
Before persisting a sealed envelope, the driver seals the plaintext with the active key, immediately unseals it, authenticates it, and asserts the round trip reproduced the original bytes. A mismatch means the ciphertext does not faithfully encode the payload — a cryptographic/keying bug or memory corruption — so the update is aborted rather than storing a lossy envelope.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:4971
hash.id
);
let raw_transaction = hash
.raw_transaction
.as_deref()
.expect("migration query requires plaintext");
let intent = load_execution_intent(&mut transaction, hash.intent_id).await?;
let context = authenticate_retained_payload(
raw_transaction,
&intent,
&hash,
keys.deployment_id(),
)
.with_context(|| format!("failed to authenticate execution payload {}", hash.id))?;
reserve_execution_payload_seal(&mut transaction, keys.active_key_id()).await?;
let envelope = keys.seal(raw_transaction, &context)?;
let unsealed = keys.unseal(&envelope, &context)?;
authenticate_retained_payload(&unsealed, &intent, &hash, keys.deployment_id())?;
anyhow::ensure!(
unsealed == raw_transaction,
"Execution payload {} changed during seal round trip",
hash.id
);
let result = sqlx::query(
"UPDATE execution_transaction_hash \
SET sealed_transaction = $2, raw_transaction = NULL, updated_at = NOW() \
WHERE id = $1 AND raw_transaction = $3 AND sealed_transaction IS NULL",
)
.bind(hash.id)
.bind(&envelope)
.bind(raw_transaction)
.execute(&mut *transaction)
.await
.context("failed to promote execution payload")?;
anyhow::ensure!(
result.rows_affected() == 1,
"Execution payload {} changed during migration",View on GitHub (pinned to 18893faf8b)
Solutions
- Verify the PayloadKeySet deployment_id and active key match the deployment that stored the payloads
- Re-run the batch with a consistent key set — if it recurs, treat the affected rows as corrupt and restore from backup
- Check for version skew between the sealing library/protocol version and the migrator binary
- Report/preserve the failing hash id and raw bytes; do not hand-edit sealed payloads
Example fix
// before: migrating with possibly stale keys let keys = load_key_set_from_env()?; // after: verify deployment id matches stored state before sealing let keys = load_key_set_from_env()?; assert_eq!(keys.deployment_id(), expected_deployment_id, "key set deployment mismatch");
Defensive patterns
Strategy: try-catch
Validate before calling
// verify key set matches the deployment before sealing
if keys.deployment_id() != expected_deployment_id {
anyhow::bail!("payload key set deployment mismatch");
} Type guard
fn keys_match_deployment(keys: &PayloadKeySet, expected: &DeploymentId) -> bool {
keys.deployment_id() == expected
} Try / catch
match db.migrate_execution_payload_batch(&keys, 500).await {
Ok(done) => {}
Err(e) if e.to_string().contains("changed during seal round trip") => {
// halt migration, preserve the failing hash id, restore keys/data before retry
}
Err(e) => return Err(e),
} Prevention
- Pin protocol/key versions across all nodes and migration tooling
- Verify key set deployment_id and active_key_id against stored state before sealing
- Never edit sealed payloads or raw bytes directly
- Restore affected rows from backup if round-trip failures repeat
When it happens
Trigger: During migrate_execution_payload_batch, keys.seal(...) followed by keys.unseal(...) returns bytes != raw_transaction for a given hash id — e.g. wrong key version in the key set, a broken codec, or nondeterministic context mismatches.
Common situations: Rotated/deployed key not matching the one used for seal; deployment_id mismatch between keys and stored intent; library/protocol version skew between writer and migrator; corrupted raw_transaction bytes.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Replacement hash {transaction_hash} conflicts with another i
- Included wrap transaction {tx_hash} has invalid block number
- WETH balance overflow for included transaction {tx_hash} at
- conflicting RTDS TWAP observation topic={} symbol={} timesta
- Continuous future chain discontinuity for {target_bar_type}:
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/5f61ebba791ccb23.
Report an issue: GitHub.