jdx/mise · critical

cannot publish: encrypted path {} has an unencrypted or inva

Error message

cannot publish: encrypted path {} has an unencrypted or invalid version in commit {commit}; encrypting the latest version does not erase earlier plaintext. Review and explicitly rewrite or replace that history before connecting it to origin; mise will not rewrite it automatically

What it means

audit_history verifies that every commit touching an encrypted path contains a validly encrypted version of that path (age-envelope with a recognized mode and the age header). If any historical version of the path is unencrypted or malformed, publishing is refused: encrypting the latest version would not erase plaintext from earlier commits, and mise will not rewrite history automatically.

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. Review and explicitly rewrite or replace that history before connecting it to origin; mise will not rewrite it automatically",
                    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 afd2eddd3a)

Solutions

  1. Rewrite history to encrypt or purge the plaintext versions (e.g. `git filter-repo` or an interactive rebase re-committing the file encrypted), then re-run publish.
  2. Alternatively start a fresh history: replace the repo/ref and push the newly encrypted baseline.
  3. Inspect `git log --follow -- <path>` in the history repo to locate the offending commit and version before rewriting.

Example fix

// before
# plaintext config committed in history, encryption enabled later
// after
git filter-repo --path secrets.env --invert-paths   # or re-commit encrypted across history
mise pull  # audit now passes
Defensive patterns

Strategy: validation

Validate before calling

# before publishing, check every historical version of the path is encrypted
git log --format=%H -- path/to/secrets.env |
  while read c; do git show "$c":path/to/secrets.env |
    head -c 22 | grep -q 'age-encryption.org/v1' || echo "plaintext in $c"; done

Try / catch

if let Err(e) = publish() {
    if e.to_string().starts_with("cannot publish: encrypted path") {
        // rewrite history (git filter-repo / rebase) before retrying
    }
}

Prevention

When it happens

Trigger: Connecting a setup-history repo to origin where an encrypted path had a plaintext (or corrupt/non-age) blob in any commit in its history; encryption enabled after the path was already tracked unencrypted.

Common situations: User enables encryption on an existing history repo; a commit snuck in with a raw config before the encrypt step; the ciphertext blob is truncated or not age-formatted.

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@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/a977735d92c03173. Report an issue: GitHub.