jdx/mise · error
cannot retain an unreadable plaintext version of newly encry
Error message
cannot retain an unreadable plaintext version of newly encrypted {} What it means
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.
Source
Thrown at src/system/history/checkpoint.rs:903
.flat_map(|path| path.ancestors().skip(1))
.collect();
for file in repo.ls_tree(parent)? {
let located = roots.locate(&file.path);
let Some(path) = located.path() else { continue };
if !omitted.iter().any(|omitted| path.starts_with(omitted))
|| !tracked.would_retain(path)?
|| repo.object_at(tree, &file.path)?.is_some()
{
continue;
}
let Some(entry) = tracked.entry_for(path) else {
continue;
};
if entry.tree_path(path)? != file.path {
continue;
}
if entry.policy.encrypt && !repo.blob_starts_with(&file.oid, b"mise-encrypted-file-v1\n")? {
eyre::bail!(
"cannot retain an unreadable plaintext version of newly encrypted {}",
display_path(path)
);
}
let display = display_path(path);
modes.remove(&display);
if let Some(mode) = record.tree.modes.get(&display) {
modes.insert(display, *mode);
}
for parent in path
.ancestors()
.skip(1)
.take_while(|parent| parent.starts_with(&entry.path))
{
if !observed_directories.contains(parent) {
let display = display_path(parent);
if let Some(mode) = record.tree.modes.get(&display) {
modes.insert(display, *mode);View on GitHub (pinned to afd2eddd3a)
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.
Example fix
// before: enable encryption over an already-tracked plaintext file [[history.encryption.files]] path = "secrets.env" encrypt = true // after: purge the plaintext preimage first, then re-track mise history checkpoint --purge secrets.env # or equivalent removal, then re-encrypt & re-snapshot
Defensive patterns
Strategy: try-catch
Validate before calling
let head = repo.blob_starts_with(&file.oid, b"mise-encrypted-file-v1\n")?;
if entry.policy.encrypt && !head { /* migrate/re-encrypt before checkpoint */ } Try / catch
match result {
Err(e) if e.to_string().contains("unreadable plaintext version") => migrate_to_encrypted(path)?,
other => other?,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- expected a checkpoint
- trust the configuration before using its encryption recipien
- no protective checkpoint could be taken; nothing was changed
- checkpoint {} has no content snapshot
- checkpoint {} did not finish; there is no state to roll back
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/7ee9d477af3145b5.
Report an issue: GitHub.