{"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).wrap_err_with(|| {\n        format!(\n            \"cannot unlock {path}; run mise dot pull interactively with a matching age identity\"\n        )\n    })?;\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","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/sync/files.rs#L240-L276","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-encrypt the file at its new path so the envelope records the correct destination.","Move the file back to the path recorded in the envelope.","Restore the file from history if it was accidentally renamed or copied."],"exampleFix":"// before: manually moving the encrypted file\nmv ~/.config/encrypted-file.bin ~/.config/renamed.bin\n// after: re-sync so the envelope is rewritten for the new path\nmise setup sync // re-encrypts at the correct path","handlingStrategy":"validation","validationCode":"// verify the envelope's recorded path before decrypting\nconst envelope = await sync.inspectEnvelope(path);\nif (envelope.path !== path) {\n  throw new Error(`re-encrypt ${path}: envelope declares ${envelope.path}`);\n}","typeGuard":"function envelopeMatchesPath(envelope: { path: string }, path: string): envelope is { path: string } {\n  return envelope.path === path;\n}","tryCatchPattern":"try {\n  await sync.decrypt(path);\n} catch (e) {\n  if (/does not match its path/.test(String(e))) {\n    await sync.encryptAt(path); // re-encrypt to bind the new path\n  } else throw e;\n}","preventionTips":["Never move or rename encrypted files directly on disk; use the sync tool.","Re-encrypt after any rename so the envelope path stays in sync.","Don't copy ciphertext blobs between paths."],"tags":["encryption","path-mismatch","integrity"],"backgroundTag":"checksum-mismatch","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}