{"record":{"id":"f1077798ae6aa339","repo":"JosefNemec/Playnite","slug":"failed-to-read-path","errorCode":null,"errorMessage":"Failed to read {path}","messagePattern":"Failed to read (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"source/Playnite/Common/FileSystem.cs","lineNumber":192,"sourceCode":"        public static string ReadFileAsStringSafe(string path, int retryAttempts = 5)\r\n        {\r\n            path = Paths.FixPathLength(path);\r\n            IOException ioException = null;\r\n            for (int i = 0; i < retryAttempts; i++)\r\n            {\r\n                try\r\n                {\r\n                    return File.ReadAllText(path);\r\n                }\r\n                catch (IOException exc)\r\n                {\r\n                    logger.Debug($\"Can't read from file, trying again. {path}\");\r\n                    ioException = exc;\r\n                    Task.Delay(500).Wait();\r\n                }\r\n            }\r\n\r\n            throw new IOException($\"Failed to read {path}\", ioException);\r\n        }\r\n\r\n        public static byte[] ReadFileAsBytesSafe(string path, int retryAttempts = 5)\r\n        {\r\n            path = Paths.FixPathLength(path);\r\n            IOException ioException = null;\r\n            for (int i = 0; i < retryAttempts; i++)\r\n            {\r\n                try\r\n                {\r\n                    return File.ReadAllBytes(path);\r\n                }\r\n                catch (IOException exc)\r\n                {\r\n                    logger.Debug($\"Can't read from file, trying again. {path}\");\r\n                    ioException = exc;\r\n                    Task.Delay(500).Wait();\r\n                }\r","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/JosefNemec/Playnite/blob/5911f4e964e628aa7a69c2030ab35101afa63067/source/Playnite/Common/FileSystem.cs#L174-L210","documentation":"Thrown by ReadFileAsStringSafe after retryAttempts (default 5) retries of File.ReadAllText all fail with IOException. Each retry waits 500 ms to tolerate transient locks; the final throw preserves the last IOException as the inner exception. It is a 'safe' read wrapper that escalates persistent IO contention to the caller.","triggerScenarios":"Calling ReadFileAsStringSafe(path) where the file is locked by another process/handle for the entire ~2.5 s retry window, or the path is genuinely unreadable (permissions, missing file, IO error).","commonSituations":"The target file is held open by another Playnite instance or extension; antivirus has an exclusive scan lock; a sync/​cloud-client (OneDrive) is mid-sync; the file was deleted between calls; permissions deny the read.","solutions":["Ensure no other process holds an exclusive lock on the file (close other Playnite instances/tools).","Retry the call after a longer delay, or call with a higher retryAttempts count.","Verify the path exists and the process has read permissions.","Exclude the file/​folder from AV real-time scanning if locking is confirmed."],"exampleFix":"// before\nstring text = FileSystem.ReadFileAsStringSafe(path);\n\n// after\nstring text;\ntry\n{\n    text = FileSystem.ReadFileAsStringSafe(path, retryAttempts: 10);\n}\ncatch (IOException e)\n{\n    logger.Error(e, $\"Could not read {path} after retries\");\n    throw;\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { return FileSystem.ReadFileAsStringSafe(path, retryAttempts: 10); }\ncatch (IOException e) { logger.Error(e, $\"Unreadable after retries: {path}\"); throw; }","preventionTips":["Avoid holding exclusive locks on files Playnite must read.","Call the *Safe overloads with enough retryAttempts for contended files.","Confirm path existence/permissions before retrying."],"tags":["filesystem","io","retry","contention","playnite"],"backgroundTag":null,"analyzedSha":"5911f4e964e628aa7a69c2030ab35101afa63067","analyzedAt":"2026-08-13T17:38:25.713Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}