{"record":{"id":"6ed209277a0920ae","repo":"jdx/mise","slug":"encrypted-file-does-not-match-its-path-or-mode-p","errorCode":null,"errorMessage":"encrypted file does not match its path or mode: {path}","messagePattern":"encrypted file does not match its path or mode: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/files.rs","lineNumber":241,"sourceCode":"    // Only encrypted envelopes have this limit; do not change plaintext sync.\n    let bytes = repo.cat_object_bounded(&object.1, limit)?;\n    let Some(body) = bytes.strip_prefix(MAGIC) else {\n        return Ok(None);\n    };\n    Ok(Some(\n        rmp_serde::from_slice(body).wrap_err(\"invalid encrypted file envelope\")?,\n    ))\n}\n\nfn validate(path: &str, outer: &Envelope, inner: &Plaintext) -> Result<()> {\n    if outer.path != path\n        || inner.path != path\n        || inner.mode != outer.mode\n        || inner.scheme != outer.scheme\n        || !layout::is_safe_branch_path(path)\n        || !matches!(inner.mode.as_str(), \"100644\" | \"100755\" | \"120000\")\n    {\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    }","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/sync/files.rs#L223-L259","documentation":"This error comes from the integrity validation of an encrypted-file envelope in mise's history sync. The decrypted inner plaintext must exactly match the outer envelope's path, mode, and scheme, use a safe branch path, and use a supported git mode; any mismatch means the envelope was tampered with, corrupted, or crafted inconsistently. It is thrown by validate() before decrypt() will produce file contents.","triggerScenarios":"Calling decrypt (via commit_object) on an encrypted blob whose inner Plaintext.path differs from the outer envelope path, whose inner mode/scheme differs from the outer, whose path fails layout::is_safe_branch_path, or whose mode is not 100644/100755/120000.","commonSituations":"Hand-edited or partially merged encrypted files in the setup store; an attacker or a bad tool rewriting the envelope metadata; a symlink or special file with an unsupported mode being encrypted; path renames done on only one side of the envelope.","solutions":["Re-pull the encrypted store from origin (mise bootstrap dotfiles pull) to replace the corrupt envelope.","Re-encrypt the file from its plaintext source so path/mode/scheme match consistently.","Check the file's path is a safe branch path and its mode is a supported git mode (100644/100755/120000).","If a merge caused the mismatch, resolve the encrypted file by taking one whole side, never hand-merging envelope bytes."],"exampleFix":"// before: hand-merged envelope with mismatched inner path\n$ git checkout --theirs .config/secrets.env.enc\n// after: replace the whole envelope from a verified side\n$ git checkout origin/history -- .config/secrets.env.enc\n$ mise bootstrap dotfiles pull","handlingStrategy":"validation","validationCode":"fn safe_to_decrypt(path: &str, mode: &str) -> bool {\n    layout::is_safe_branch_path(path)\n        && matches!(mode, \"100644\" | \"100755\" | \"120000\")\n}","typeGuard":null,"tryCatchPattern":"match decrypt(repo, &object, path, interactive) {\n    Ok(obj) => apply(obj),\n    Err(e) if e.to_string().contains(\"does not match its path or mode\") => {\n        eprintln!(\"envelope integrity failed; re-pulling from origin\");\n        repull_from_origin(path);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never hand-edit or hand-merge encrypted envelope files; always resolve to one whole side.","Re-encrypt (push) after renaming or moving encrypted files.","Only track regular files, executables, and symlinks in encrypted sources.","Verify store integrity (pull from origin) after restores or history rewrites."],"tags":["encryption","integrity","dotfiles","sync"],"backgroundTag":"checksum-mismatch","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}