{"record":{"id":"7ee9d477af3145b5","repo":"jdx/mise","slug":"cannot-retain-an-unreadable-plaintext-version-of-n","errorCode":null,"errorMessage":"cannot retain an unreadable plaintext version of newly encrypted {}","messagePattern":"cannot retain an unreadable plaintext version of newly encrypted (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/checkpoint.rs","lineNumber":903,"sourceCode":"        .flat_map(|path| path.ancestors().skip(1))\n        .collect();\n    for file in repo.ls_tree(parent)? {\n        let located = roots.locate(&file.path);\n        let Some(path) = located.path() else { continue };\n        if !omitted.iter().any(|omitted| path.starts_with(omitted))\n            || !tracked.would_retain(path)?\n            || repo.object_at(tree, &file.path)?.is_some()\n        {\n            continue;\n        }\n        let Some(entry) = tracked.entry_for(path) else {\n            continue;\n        };\n        if entry.tree_path(path)? != file.path {\n            continue;\n        }\n        if entry.policy.encrypt && !repo.blob_starts_with(&file.oid, b\"mise-encrypted-file-v1\\n\")? {\n            eyre::bail!(\n                \"cannot retain an unreadable plaintext version of newly encrypted {}\",\n                display_path(path)\n            );\n        }\n        let display = display_path(path);\n        modes.remove(&display);\n        if let Some(mode) = record.tree.modes.get(&display) {\n            modes.insert(display, *mode);\n        }\n        for parent in path\n            .ancestors()\n            .skip(1)\n            .take_while(|parent| parent.starts_with(&entry.path))\n        {\n            if !observed_directories.contains(parent) {\n                let display = display_path(parent);\n                if let Some(mode) = record.tree.modes.get(&display) {\n                    modes.insert(display, *mode);","sourceCodeStart":885,"sourceCodeEnd":921,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/checkpoint.rs#L885-L921","documentation":"During checkpoint preimage retention (`retain_omitted`), mise refuses to keep an omitted file as a plaintext copy when its policy says the file must be encrypted and the stored blob is not in the `mise-encrypted-file-v1` format. Keeping a readable plaintext copy of a newly encrypted file would defeat the encryption policy, so the checkpoint aborts.","triggerScenarios":"A path's checkpoint policy has `policy.encrypt = true`, the file's oid blob in the repo does NOT start with `mise-encrypted-file-v1\\n`, and the path is being omitted from the current checkpoint while retention tries to preserve it.","commonSituations":"A user recently added `[history.encryption]` recipients for a config file that was previously tracked in plaintext; the old plaintext blob still exists in the checkpoint store and the retention step detects the mismatch.","solutions":["Re-initialize or re-snapshot the checkpoint store so the file is captured in encrypted form under the new policy.","Remove the file from history tracking (or its preimage entry) so no plaintext preimage is retained, then re-add it after encryption.","Temporarily disable encryption policy for the path if plaintext retention is intended, then re-enable after migrating."],"exampleFix":"// before: enable encryption over an already-tracked plaintext file\n[[history.encryption.files]]\npath = \"secrets.env\"\nencrypt = true\n// after: purge the plaintext preimage first, then re-track\nmise history checkpoint --purge secrets.env  # or equivalent removal, then re-encrypt & re-snapshot","handlingStrategy":"try-catch","validationCode":"let head = repo.blob_starts_with(&file.oid, b\"mise-encrypted-file-v1\\n\")?;\nif entry.policy.encrypt && !head { /* migrate/re-encrypt before checkpoint */ }","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"unreadable plaintext version\") => migrate_to_encrypted(path)?,\n    other => other?,\n}","preventionTips":["Re-snapshot files after changing encryption policy","Purge plaintext preimages when enabling encryption on tracked files","Verify stored blobs carry the mise-encrypted-file-v1 header after policy changes"],"tags":["encryption","history","checkpoint","security"],"backgroundTag":"encryption-policy-violation","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}