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

  1. 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.
  2. Otherwise remove/rewrite the offending history (prune or rebase the history store so the plaintext commit is gone), then push again.
  3. Identify the leaking commit (named in the message) and re-encrypt or drop that version of the path.
  4. 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

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


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)