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

  1. Verify the PayloadKeySet deployment_id and active key match the deployment that stored the payloads
  2. Re-run the batch with a consistent key set — if it recurs, treat the affected rows as corrupt and restore from backup
  3. Check for version skew between the sealing library/protocol version and the migrator binary
  4. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/5f61ebba791ccb23. Report an issue: GitHub.