jdx/mise · error

encrypted file does not match its path

Error message

encrypted file does not match its path: {path}

What it means

Thrown by `decrypt` when the path recorded inside the encrypted envelope (`outer.path`) does not match the path at which the encrypted file was actually found. The envelope is tamper-evident: each encrypted object is bound to its declared destination path, so a mismatch means the file was moved, renamed, or pointed at the wrong location.

Solutions

  1. Re-encrypt the file at its new path so the envelope records the correct destination.
  2. Move the file back to the path recorded in the envelope.
  3. Restore the file from history if it was accidentally renamed or copied.

Example fix

// before: manually moving the encrypted file
mv ~/.config/encrypted-file.bin ~/.config/renamed.bin
// after: re-sync so the envelope is rewritten for the new path
mise setup sync // re-encrypts at the correct path
Defensive patterns

Strategy: validation

Validate before calling

// verify the envelope's recorded path before decrypting
const envelope = await sync.inspectEnvelope(path);
if (envelope.path !== path) {
  throw new Error(`re-encrypt ${path}: envelope declares ${envelope.path}`);
}

Type guard

function envelopeMatchesPath(envelope: { path: string }, path: string): envelope is { path: string } {
  return envelope.path === path;
}

Try / catch

try {
  await sync.decrypt(path);
} catch (e) {
  if (/does not match its path/.test(String(e))) {
    await sync.encryptAt(path); // re-encrypt to bind the new path
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `decrypt` (via `commit_object`) on a file whose decrypted `Plaintext.path` differs from the `path` argument — e.g. after `git mv`/manual rename of an encrypted file without re-encrypting, or copying an encrypted blob to another path.

Common situations: Renaming or relocating an encrypted dotfile on disk directly; symlinking one encrypted file at another path; merging histories where the same ciphertext landed at a different path.

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/15b90ebf5e487ee1. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/sync/files.rs:258

    {
        bail!("encrypted file does not match its path or mode: {path}");
    }
    Ok(())
}

pub(crate) fn decrypt(
    repo: &HistoryRepo,
    path: &str,
    object: &Object,
    interactive: bool,
) -> Result<Object> {
    let outer = envelope(repo, object, agecrypt::MAX_ENCRYPTED_BYTES)?
        .ok_or_else(|| eyre::eyre!("missing encrypted file envelope: {path}"))?;
    if control_file(path) {
        bail!("setup configuration itself cannot be encrypted: {path}");
    }
    if outer.path != path {
        bail!("encrypted file does not match its path: {path}");
    }
    if let Some(decrypted) = repo.decrypted_object(&object.1) {
        return Ok(decrypted);
    }
    let bytes = agecrypt::decrypt_sync(&outer.ciphertext.0, interactive).wrap_err_with(|| {
        format!(
            "cannot unlock {path}; run mise dot pull interactively with a matching age identity"
        )
    })?;
    let inner: Plaintext =
        rmp_serde::from_slice(&bytes).wrap_err("invalid encrypted file payload")?;
    validate(path, &outer, &inner)?;
    let oid = repo.transient_blob_id(&inner.content.0)?;
    let decrypted = (inner.mode, oid);
    repo.remember_decrypted(&object.1, decrypted.clone());
    Ok(decrypted)
}

View on GitHub (pinned to 533346cc37)