{"record":{"id":"0197b0d1b6674217","repo":"BigPizzaV3/CodexPlusPlus","slug":"unexpected-backup-file-path-path","errorCode":null,"errorMessage":"unexpected backup file path: {path}","messagePattern":"unexpected backup file path: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-data/src/storage.rs","lineNumber":1098,"sourceCode":"        .collect::<Vec<_>>();\n    if table == \"messages\" && keys.is_empty() {\n        row.get_key_value(\"session_id\")\n            .map(|(key, _)| vec![key])\n            .unwrap_or_default()\n    } else {\n        keys\n    }\n}\n\nfn detect_file_restore_conflicts(tables: &Map<String, Value>) -> anyhow::Result<()> {\n    let Some(files) = tables.get(\"__files\").and_then(Value::as_array) else {\n        return Ok(());\n    };\n    let allowed_paths = allowed_backup_file_paths(tables);\n    for file in files {\n        if let Some(path) = file.get(\"path\").and_then(Value::as_str) {\n            if !allowed_paths.contains(path) {\n                anyhow::bail!(\"unexpected backup file path: {path}\");\n            }\n            if Path::new(path).exists() {\n                anyhow::bail!(\"restore conflict: file already exists: {path}\");\n            }\n            if let Some(content) = file.get(\"content_b64\").and_then(Value::as_str) {\n                base64::Engine::decode(&base64::engine::general_purpose::STANDARD, content)?;\n            }\n        }\n    }\n    Ok(())\n}\n\nfn allowed_backup_file_paths(tables: &Map<String, Value>) -> HashSet<String> {\n    tables\n        .get(\"threads\")\n        .and_then(Value::as_array)\n        .into_iter()\n        .flatten()","sourceCodeStart":1080,"sourceCodeEnd":1116,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/f2074595a281bc057525c748175c8eb9805b0673/crates/codex-plus-data/src/storage.rs#L1080-L1116","documentation":"Thrown by detect_file_restore_conflicts when a __files entry's path is not among the allowed backup file paths derived from the same backup. This confines file restores to the thread-rollouts locations the backup legitimately references; the offending input is the {path} field inside the backup's __files array (a traversal or foreign-path attempt).","triggerScenarios":"Thrown at crates/codex-plus-data/src/storage.rs:1098 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["确认备份文件未被篡改、其 __files 路径与 rollouts 记录一致","使用本应用生成的原始 undo token","为新类型的文件输出补充 allowed_backup_file_paths 推导规则","从备份中移除越界文件条目后重试"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"f2074595a281bc057525c748175c8eb9805b0673","analyzedAt":"2026-08-23T12:52:24.489Z","contentChangedAt":"2026-08-23T12:52:24.489Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}