{"record":{"id":"534093c34fe4bfcb","repo":"nautechsystems/nautilus_trader","slug":"execution-payload-changed-during-rewrap","errorCode":null,"errorMessage":"Execution payload {} changed during rewrap","messagePattern":"Execution payload (.+?) changed during rewrap","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":5508,"sourceCode":"                anyhow::anyhow!(\n                    \"Execution payload {} has no envelope during rewrap\",\n                    hash.id\n                )\n            })?;\n            anyhow::ensure!(\n                hash.raw_transaction.is_none(),\n                \"Execution payload {} contains plaintext during rewrap\",\n                hash.id\n            );\n            let intent = load_execution_intent(&mut transaction, hash.intent_id).await?;\n            let context = payload_context(&intent, &hash, keys.deployment_id())?;\n            let raw_transaction = keys.unseal(envelope, &context)?;\n            authenticate_retained_payload(&raw_transaction, &intent, &hash, keys.deployment_id())?;\n            reserve_execution_payload_seal(&mut transaction, keys.active_key_id()).await?;\n            let rewrapped = keys.seal(&raw_transaction, &context)?;\n            let verified = keys.unseal(&rewrapped, &context)?;\n            authenticate_retained_payload(&verified, &intent, &hash, keys.deployment_id())?;\n            anyhow::ensure!(\n                verified == raw_transaction,\n                \"Execution payload {} changed during rewrap\",\n                hash.id\n            );\n            let result = sqlx::query(\n                \"UPDATE execution_transaction_hash SET sealed_transaction = $2, updated_at = NOW() \\\n                 WHERE id = $1 AND sealed_transaction = $3 AND raw_transaction IS NULL\",\n            )\n            .bind(hash.id)\n            .bind(&rewrapped)\n            .bind(envelope)\n            .execute(&mut *transaction)\n            .await\n            .context(\"failed to persist rewrapped execution payload\")?;\n            anyhow::ensure!(\n                result.rows_affected() == 1,\n                \"Execution payload {} changed during rewrap\",\n                hash.id","sourceCodeStart":5490,"sourceCodeEnd":5526,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L5490-L5526","documentation":"During a key-rotation rewrap of a stored execution payload, the payload is unsealed, re-sealed with the new key, unsealed again, and compared byte-for-byte with the original. This error is thrown when the verified plaintext differs from the original raw transaction, meaning the seal/unseal round-trip was not lossless and persisting the rewrapped payload would corrupt it.","triggerScenarios":"Calling the rewrap routine (within the execution payload key-rotation flow in database.rs) when the cipher or key context used by keys.seal/keys.unseal does not reproduce the exact payload — e.g. a seal implementation that re-encodes the transaction (different serialization, altered metadata) or mismatched deployment_id context between seal and unseal.","commonSituations":"Upgrading the sealing codec or key-management code so old payloads no longer round-trip identically; swapping serialization libraries or transaction encoding versions; testing with a custom PayloadKeySet whose seal alters the payload; concurrent schema/code versions where the rewrap worker runs newer code than the writer.","solutions":["Inspect keys.seal/keys.unseal to ensure unseal(seal(x)) == x exactly; fix any re-serialization or normalization inside seal","Verify the seal context (deployment_id, protocol version) matches the context used for unseal","Add a round-trip unit test over representative raw transactions to catch encoding drift before rotation runs","Roll back the transaction (the ensure! aborts before UPDATE) and re-run rotation after fixing the codec"],"exampleFix":"// before: seal re-encodes the payload\nfn seal(&self, raw: &[u8], ctx: &Context) -> Result<Vec<u8>> {\n    let tx = Transaction::decode(raw)?; // lossy re-encode\n    self.cipher.encrypt(&tx.encode(), ctx)\n}\n// after: seal is byte-preserving\nfn seal(&self, raw: &[u8], ctx: &Context) -> Result<Vec<u8>> {\n    self.cipher.encrypt(raw, ctx)\n}","handlingStrategy":"validation","validationCode":"// Round-trip check before scheduling rotation for a payload\nlet rewrapped = keys.seal(&raw, &ctx)?;\nassert_eq!(keys.unseal(&rewrapped, &ctx)?, raw, \"seal round-trip not byte-preserving\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep seal/unseal byte-preserving: never decode-and-re-encode the payload inside seal","Pin the sealing codec version and test round-trips across upgrades","Include deployment_id/protocol version in the seal context consistently"],"tags":["rust","encryption","key-rotation","data-integrity"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}