{"record":{"id":"15b90ebf5e487ee1","repo":"jdx/mise","slug":"encrypted-file-does-not-match-its-path-path","errorCode":null,"errorMessage":"encrypted file does not match its path: {path}","messagePattern":"encrypted file does not match its path: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/files.rs","lineNumber":258,"sourceCode":"    {\n        bail!(\"encrypted file does not match its path or mode: {path}\");\n    }\n    Ok(())\n}\n\npub(crate) fn decrypt(\n    repo: &HistoryRepo,\n    path: &str,\n    object: &Object,\n    interactive: bool,\n) -> Result<Object> {\n    let outer = envelope(repo, object, agecrypt::MAX_ENCRYPTED_BYTES)?\n        .ok_or_else(|| eyre::eyre!(\"missing encrypted file envelope: {path}\"))?;\n    if control_file(path) {\n        bail!(\"setup configuration itself cannot be encrypted: {path}\");\n    }\n    if outer.path != path {\n        bail!(\"encrypted file does not match its path: {path}\");\n    }\n    if let Some(decrypted) = repo.decrypted_object(&object.1) {\n        return Ok(decrypted);\n    }\n    let bytes = agecrypt::decrypt_sync(&outer.ciphertext.0, interactive)\n        .wrap_err_with(|| format!(\"cannot unlock {path}; run mise bootstrap dotfiles pull interactively with a matching age identity\"))?;\n    let inner: Plaintext =\n        rmp_serde::from_slice(&bytes).wrap_err(\"invalid encrypted file payload\")?;\n    validate(path, &outer, &inner)?;\n    let oid = repo.transient_blob_id(&inner.content.0)?;\n    let decrypted = (inner.mode, oid);\n    repo.remember_decrypted(&object.1, decrypted.clone());\n    Ok(decrypted)\n}\n\nfn encrypt(\n    repo: &HistoryRepo,\n    path: &str,","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/sync/files.rs#L240-L276","documentation":"The outer encrypted envelope records the logical path of the file it protects. If the envelope's embedded path does not equal the path at which the object was found, decrypt() rejects it, because decrypting would place contents under an identity that does not match the store's record. This prevents path-swap attacks and accidental misplacement.","triggerScenarios":"Calling decrypt (via commit_object) on an object whose envelope(outer.path) differs from the requested path — e.g. the file was moved/renamed without re-encrypting, or envelope bytes were copied to another path.","commonSituations":"git mv on an encrypted file without re-encryption; cherry-pick/merge that relocated an encrypted blob; manually copying an .enc file to a new name.","solutions":["Re-encrypt the file at its new path so the envelope path matches (run the encrypt step of the dotfiles sync).","Move the file back to its original path, or restore it from origin.","Run mise bootstrap dotfiles pull to resync envelopes with the authoritative history."],"exampleFix":"// before: envelope for ~/.gitconfig placed at ~/.gitconfig.bak\n$ git mv ~/.gitconfig ~/.gitconfig.bak\n// after: re-encrypt under the new path so outer.path matches\n$ mise bootstrap dotfiles push  # re-encrypts and records the new path","handlingStrategy":"validation","validationCode":"if envelope_path_for(blob) != target_path {\n    eprintln!(\"re-encrypt needed: envelope path mismatch\");\n}","typeGuard":null,"tryCatchPattern":"match decrypt(repo, &object, path, interactive) {\n    Ok(obj) => obj,\n    Err(e) if e.to_string().contains(\"does not match its path\") => {\n        eprintln!(\"{path} was moved; re-encrypting under new path\");\n        reencrypt(path)\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Use the sync's own move/rename flow instead of git mv on encrypted files.","Re-push (re-encrypt) after any path change.","Avoid copying .enc blobs to new filenames manually."],"tags":["encryption","integrity","path-mismatch"],"backgroundTag":"checksum-mismatch","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}