jdx/mise · critical
cannot publish: encrypted path
Error message
cannot publish: encrypted path {} has an unencrypted or invalid version in commit {commit}; encrypting the latest version does not erase earlier plaintext. Remove that history before pushing, or use `mise dot sync --allow-plaintext-history` to publish it anyway What it means
When publishing (pushing) history, audit_history_cached walks every commit reachable from the remote head and checks that each version of a path marked as encrypted is a genuine, well-formed age-encrypted envelope (correct path, regular-file/symlink mode, and the age-encryption.org/v1 header). If any historical commit contains a plaintext or corrupt version of an encrypted path, publishing is blocked — because pushing would leak plaintext that 'encrypting the latest version' cannot erase. The user must rewrite/remove that history or explicitly opt in with --allow-plaintext-history.
Solutions
- If the plaintext history is safe to keep and you accept the risk, re-run with `mise dot sync --allow-plaintext-history` to publish anyway.
- Otherwise remove/rewrite the offending history (prune or rebase the history store so the plaintext commit is gone), then push again.
- Identify the leaking commit (named in the message) and re-encrypt or drop that version of the path.
- Going forward, enable encryption for a path before its first commit so plaintext versions never enter history.
Example fix
// before $ mise dot sync # push error: cannot publish: encrypted path .envrc has an unencrypted or invalid version in commit abc123... // after (explicit opt-in) $ mise dot sync --allow-plaintext-history // or: rewrite history to drop commit abc123, then $ mise dot sync
Defensive patterns
Strategy: validation
Validate before calling
# before pushing, confirm no plaintext versions of encrypted paths exist in history mise dot log -- <encrypted-path> # or inspect history commits; require all versions to be age envelopes # explicit acknowledgement if intentional: mise dot sync --allow-plaintext-history
Prevention
- Enable encryption for a path before its first commit to history.
- Periodically audit history for plaintext versions of secret paths before pushing.
- Never silence with --allow-plaintext-history unless you accept leaking old plaintext.
- Treat 'encrypt after the fact' as insufficient — rewrite history to drop plaintext commits.
When it happens
Trigger: `mise dot sync` push with audit_history_cached detecting, for some commit {commit}, an entry whose path is in the encrypted set but whose blob is not a valid envelope (unencrypted plaintext, wrong mode, or ciphertext missing the age header). Typically a file was committed before encryption was enabled for that path.
Common situations: You enabled encryption for a secrets file after it had already been committed in plaintext to dot history; a corrupted/interrupted encryption write left a bad blob in an old commit; a history import brought in pre-encryption versions.
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
- cannot publish: encrypted path
- trust the configuration before using its encryption…
- a parent directory is now a symlink; left untouched
- app Info.plist must be a regular file
- backend is disabled by disable_backends
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/bb2a715ea2b31afd.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/sync/files.rs:131
if !protected.iter().any(|path| {
entry.path == *path
|| entry
.path
.strip_prefix(path)
.is_some_and(|rest| rest.starts_with('/'))
}) || !checked.insert((entry.path.clone(), entry.mode.clone(), entry.oid.clone()))
{
continue;
}
let object = (entry.mode, entry.oid);
let valid =
envelope(repo, &object, agecrypt::MAX_ENCRYPTED_BYTES)?.is_some_and(|outer| {
outer.path == entry.path
&& matches!(outer.mode.as_str(), "100644" | "100755" | "120000")
&& outer.ciphertext.0.starts_with(b"age-encryption.org/v1\n")
});
if !valid {
bail!(
"cannot publish: encrypted path {} has an unencrypted or invalid version in commit {commit}; encrypting the latest version does not erase earlier plaintext. Remove that history before pushing, or use `mise dot sync --allow-plaintext-history` to publish it anyway",
entry.path
);
}
}
}
if let Ok(mut cache) = AUDITS.lock() {
// One watcher normally uses one repository. Bound temporary onboarding
// probes without adding another durable history or state file.
if cache.len() >= 64 && !cache.contains_key(repo.dir()) {
cache.pop_first();
}
cache.insert(
repo.dir().to_path_buf(),
VerifiedAncestry {
head: head.into(),
protected,
},View on GitHub (pinned to 533346cc37)