{"record":{"id":"a7d0aff100f40058","repo":"babalae/better-genshin-impact","slug":"error-a7d0af","errorCode":null,"errorMessage":"文件未找到","messagePattern":"文件未找到","errorType":"exception","errorClass":"FileNotFoundException","httpStatus":null,"severity":"warning","filePath":"BetterGenshinImpact/GameTask/LogParse/TravelsDiaryDetailManager.cs","lineNumber":232,"sourceCode":"        };\n        string jsonString = JsonSerializer.Serialize(apiResponse, options);\n        string directory = Path.GetDirectoryName(path);\n\n        // 如果目录不存在，则创建它\n        if (!Directory.Exists(directory))\n        {\n            Directory.CreateDirectory(directory);\n        }\n\n        // 将格式化后的 JSON 写入文件\n        File.WriteAllText(path, jsonString);\n    }\n\n    static bool IsFileModifiedThisMonth(string filePath)\n    {\n        if (!File.Exists(filePath))\n        {\n            throw new FileNotFoundException(\"文件未找到\", filePath);\n        }\n\n        // File.GetLastWriteTime 返回 DateTime 类型为 DateTimeKind.Local\n        DateTimeOffset lastModified =\n            new DateTimeOffset(File.GetLastWriteTime(filePath)).ToOffset(ServerTimeHelper.GetServerTimeOffset());\n        \n        // 获取当前月份的开始和结束日期\n        DateTimeOffset now = ServerTimeHelper.GetServerTimeNow();\n        DateTimeOffset startOfMonth = new DateTimeOffset(now.Year, now.Month, 1, 0, 0, 0, now.Offset);\n        DateTimeOffset endOfMonth = startOfMonth.AddMonths(1).AddDays(-1);\n\n        // 判断文件最后修改时间是否在本月\n        return lastModified >= startOfMonth && lastModified <= endOfMonth;\n    }\n\n    static List<(int year, int month)> GetCurrentAndPreviousTwoMonths()\n    {\n        List<(int year, int month)> months = new List<(int year, int month)>();","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/LogParse/TravelsDiaryDetailManager.cs#L214-L250","documentation":"Thrown by TravelsDiaryDetailManager.IsFileModifiedThisMonth when the given filePath does not exist on disk. The method is called during incremental update logic to check whether a travels diary JSON file was already modified this month, and it expects the file to exist.","triggerScenarios":"Calling IsFileModifiedThisMonth(filePath) where filePath was not confirmed to exist beforehand. In the calling code (UpdateTravelsDiaryDetailManager), the file existence is checked before the call only in the i > 0 branch via fileExists, but the method itself redundantly checks and throws if the file vanished between the check and the call (race condition or logic gap).","commonSituations":"The file was deleted between the File.Exists check and the call (TOCTOU race); the caller passes a path that was never validated; running in an environment where the log directory was cleaned up.","solutions":["Return false instead of throwing when the file does not exist — a missing file logically means it was not modified this month.","Ensure the caller always checks File.Exists before calling IsFileModifiedThisMonth, or fold the existence check into the method as a non-throwing guard.","Handle the FileNotFoundException at the call site in UpdateTravelsDiaryDetailManager."],"exampleFix":"// before\nstatic bool IsFileModifiedThisMonth(string filePath)\n{\n    if (!File.Exists(filePath))\n    {\n        throw new FileNotFoundException(\"文件未找到\", filePath);\n    }\n    // ...\n}\n\n// after — missing file means not modified this month\nstatic bool IsFileModifiedThisMonth(string filePath)\n{\n    if (!File.Exists(filePath))\n    {\n        return false;\n    }\n    // ...\n}","handlingStrategy":"validation","validationCode":"// Return false instead of throwing\nstatic bool IsFileModifiedThisMonth(string filePath)\n{\n    if (!File.Exists(filePath)) return false;\n    // ... rest of logic\n}","typeGuard":null,"tryCatchPattern":"bool modified;\ntry { modified = IsFileModifiedThisMonth(tddfile); }\ncatch (FileNotFoundException) { modified = false; }","preventionTips":["Always check File.Exists before calling file-inspection methods.","Design file-checking methods to return a sentinel (false) rather than throw on missing files.","Handle TOCTOU races by catching FileNotFoundException at the call site."],"tags":["file-not-found","log-parse","defensive-coding"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}