{"record":{"id":"0722ec58116d9a68","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-home-codex-home","errorCode":null,"errorMessage":"拒绝删除 CODEX_HOME 的祖先目录 {}（CODEX_HOME = {}）","messagePattern":"拒绝删除 CODEX_HOME 的祖先目录 (.+?)（CODEX_HOME = (.+?)）","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/codex-plus-core/src/codex_home.rs","lineNumber":46,"sourceCode":"\n    // 根路径 = 有根前缀且没有父目录，覆盖 POSIX 根（`/`）与 Windows 的各种写法\n    // （`C:\\`、`\\\\?\\C:\\`、UNC `\\\\server\\share\\`）。\n    //\n    // 不能只与 `Path::new(\"/\")` 比较：Windows 上 `/` 不是绝对路径，会被 normalize\n    // 成当前盘符根（如 `C:\\`），相等比较拦不住它——也就是说递归删除盘符根本可以\n    // 绕过这道守卫。`has_root()` 这一半也不可省：没有它 `C:` 会被误判成根。\n    if target.as_os_str().is_empty() || is_filesystem_root(&target) {\n        anyhow::bail!(\"拒绝删除文件系统根目录：{}\", target.display());\n    }\n    let home = normalize_for_comparison(codex_home);\n    if target == home {\n        anyhow::bail!(\n            \"拒绝递归删除 CODEX_HOME 本身（{}）——这会连同全部会话历史一起丢失\",\n            target.display()\n        );\n    }\n    if home.starts_with(&target) {\n        anyhow::bail!(\n            \"拒绝删除 CODEX_HOME 的祖先目录 {}（CODEX_HOME = {}）\",\n            target.display(),\n            home.display()\n        );\n    }\n    Ok(())\n}\n\nfn is_filesystem_root(path: &Path) -> bool {\n    path.has_root() && path.parent().is_none()\n}\n\n/// 规范化到可比较的形态。\n///\n/// 关键点：**必须两边用同一种方式规范化**。如果待删路径能 canonicalize、而 home\n/// 不存在只能走词法，两边就会一个带 `/private` 前缀一个不带（macOS 的 `/var` →\n/// `/private/var` 就是这种），比较直接失效、守卫形同虚设。\n///","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/codex_home.rs#L28-L64","documentation":"ensure_safe_recursive_removal is a data-loss guard invoked before any recursive delete. It throws this error when the deletion target is an ancestor directory of CODEX_HOME (e.g. the user's home directory or drive root parent), meaning deleting it would also destroy the entire CODEX_HOME tree including all session history. It is a source-agnostic last line of defense added after incident #2146 where 454 MB of history was permanently lost.","triggerScenarios":"Calling ensure_safe_recursive_removal(target, codex_home) where, after normalization, home.starts_with(target) is true — i.e. target is a parent (any level) of CODEX_HOME, such as passing ~/.config when CODEX_HOME=~/.config/codex, or an env var / corrupted path that resolved to an ancestor.","commonSituations":"CODEX_HOME env var changed or resolved unexpectedly (e.g. set to ~/ or an empty-ish value falling back to a default under the target), cleanup routines computing the wrong base directory, path aliasing via symlinks or `..` segments that normalize the target into an ancestor, macOS /var vs /private/var canonicalization surprises.","solutions":["Inspect the target being deleted — it must be a leaf directory BELOW CODEX_HOME (or unrelated to it), never a parent","Check the CODEX_HOME value (env var or profile setting) for mistakes like ~/ or a drive/mount root","Verify how the target path was constructed; remove any `..` components or symlinked segments that resolve it above CODEX_HOME","If you genuinely need to delete the whole CODEX_HOME, do not route it through this guarded API — the guard is intentionally irreversible"],"exampleFix":"// before\nensure_safe_recursive_removal(&home_dir.join(\".codex/..\"), &codex_home)?;\n// after\nlet session_dir = codex_home.join(\"sessions\").canonicalize()?;\nensure_safe_recursive_removal(&session_dir, &codex_home)?;","handlingStrategy":"validation","validationCode":"fn is_safe_removal_target(target: &Path, codex_home: &Path) -> bool {\n    let t = target.canonicalize().unwrap_or_else(|_| target.to_path_buf());\n    let h = codex_home.canonicalize().unwrap_or_else(|_| codex_home.to_path_buf());\n    t != h && !h.starts_with(&t) && t.as_os_str() != \"/\"\n}\n// call ensure_safe_recursive_removal only if this returns true","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never construct deletion targets by trimming/parenting CODEX_HOME paths","Canonicalize targets before any recursive delete","Keep CODEX_HOME as a dedicated directory, not directly under directories you clean","Test cleanup code with CODEX_HOME set to unusual values (~/, roots, symlinked paths)"],"tags":["filesystem","safety-guard","data-loss-prevention","path-validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}