{"record":{"id":"f2aaeaf6c33c94c9","repo":"nautechsystems/nautilus_trader","slug":"protected-execution-transaction-contains-plaint","errorCode":null,"errorMessage":"Protected execution transaction {} contains plaintext","messagePattern":"Protected execution transaction (.+?) contains plaintext","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":5225,"sourceCode":"\n            for hash in &rows {\n                cursor = hash.id;\n                plaintext_rows += u64::from(hash.raw_transaction.is_some());\n                if !hash.payload_expected {\n                    replacement_rows += 1;\n                    anyhow::ensure!(\n                        hash.raw_transaction.is_none() && hash.sealed_transaction.is_none(),\n                        \"Replacement execution transaction {} retains signed bytes\",\n                        hash.id\n                    );\n                    continue;\n                }\n                original_rows += 1;\n                let intent = load_execution_intent(&mut transaction, hash.intent_id).await?;\n                let raw_transaction = if let (Some(keys), Some(deployment_id)) =\n                    (keys, deployment_id.as_deref())\n                {\n                    anyhow::ensure!(\n                        hash.raw_transaction.is_none(),\n                        \"Protected execution transaction {} contains plaintext\",\n                        hash.id\n                    );\n                    let envelope = hash.sealed_transaction.as_deref().ok_or_else(|| {\n                        anyhow::anyhow!(\n                            \"Protected execution transaction {} has no envelope\",\n                            hash.id\n                        )\n                    })?;\n                    let key_id = envelope_key_id(envelope)?;\n                    anyhow::ensure!(\n                        keys.contains_key(&key_id),\n                        \"Execution transaction {} requires an unavailable payload key\",\n                        hash.id\n                    );\n                    key_ids.insert(alloy::hex::encode(key_id));\n                    let context = payload_context(&intent, hash, deployment_id)?;","sourceCodeStart":5207,"sourceCodeEnd":5243,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L5207-L5243","documentation":"When payload-protection keys and a deployment id are available, the loader treats execution transactions as protected: rows must store only sealed envelopes, never plaintext signed bytes. This ensure! fires when a row's raw_transaction column is non-NULL even though protection is active, aborting the load to avoid exposing plaintext signing material.","triggerScenarios":"Calling the execution-transaction load path with keys and deployment_id supplied while the scanned row still has a non-NULL raw_transaction (e.g. a row written before protection was enabled and never rewrapped).","commonSituations":"Protection was recently enabled but the rewrap of legacy plaintext rows did not complete; rows inserted by an older node version; keys configured for the wrong deployment so rewrap skipped them.","solutions":["Run rewrap_execution_payload_storage with the correct PayloadKeySet to seal the remaining plaintext rows into envelopes.","Verify the deployment_id supplied to the load matches the deployment_id recorded in execution_payload_state.","For rows that should remain legacy (no protection), ensure keys/deployment_id are not passed so the legacy branch is taken instead.","Check that payload protection setup (begin_execution_payload_rewrap) completed fully before loading."],"exampleFix":"// Load before rewrap (protection active, row still plaintext) -> error\nload_execution_transactions(&pool, Some(&keys), Some(deployment_id)).await?;\n// after: complete rewrap first\nrewrap.rewrap_execution_payload_storage(&keys, 500).await?;\nload_execution_transactions(&pool, Some(&keys), Some(deployment_id)).await?;","handlingStrategy":"validation","validationCode":"-- Rows still plaintext while protection is active\nSELECT id FROM execution_transactions\nWHERE raw_transaction IS NOT NULL\n  AND (SELECT deployment_id FROM execution_payload_state WHERE component='signed_transactions') IS NOT NULL;","typeGuard":null,"tryCatchPattern":"match load_result {\n    Err(e) if e.to_string().contains(\"contains plaintext\") => {\n        run_payload_rewrap(&keys).await?;\n        retry_load().await\n    }\n    other => other,\n}","preventionTips":["Always run the rewrap to completion immediately after enabling payload protection.","Gate the loader on protection being active: refuse legacy loads when execution_payload_state exists.","Monitor for rows written by nodes running pre-protection versions."],"tags":["database","encryption","payload-protection","rust"],"backgroundTag":"invalid-state-transition","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"}