{"record":{"id":"1fa6257ba2532ef6","repo":"Kareadita/Kavita","slug":"archivepath-does-not-exist-on-disk","errorCode":null,"errorMessage":"{archivePath} does not exist on disk","messagePattern":"(.+?) does not exist on disk","errorType":"exception","errorClass":"KavitaException","httpStatus":null,"severity":"error","filePath":"Kavita.Services/ArchiveService.cs","lineNumber":526,"sourceCode":"\n    /// <summary>\n    /// Extracts an archive to a temp cache directory. Returns path to new directory. If temp cache directory already exists,\n    /// will return that without performing an extraction. Returns empty string if there are any invalidations which would\n    /// prevent operations to perform correctly (missing archivePath file, empty archive, etc).\n    /// </summary>\n    /// <param name=\"archivePath\">A valid file to an archive file.</param>\n    /// <param name=\"extractPath\">Path to extract to</param>\n    /// <returns></returns>\n    public void ExtractArchive(string archivePath, string extractPath)\n    {\n        if (!IsValidArchive(archivePath)) return;\n\n        if (directoryService.FileSystem.Directory.Exists(extractPath)) return;\n\n        if (!directoryService.FileSystem.File.Exists(archivePath))\n        {\n            logger.LogError(\"{Archive} does not exist on disk\", archivePath);\n            throw new KavitaException($\"{archivePath} does not exist on disk\");\n        }\n\n        var sw = Stopwatch.StartNew();\n\n        try\n        {\n            var libraryHandler = CanOpen(archivePath);\n            switch (libraryHandler)\n            {\n                case ArchiveLibrary.Default:\n                {\n                    using var archive = ZipFile.OpenRead(archivePath);\n                    ExtractArchiveEntries(archive, extractPath);\n                    break;\n                }\n                case ArchiveLibrary.SharpCompress:\n                {\n                    using var archive = ArchiveFactory.OpenArchive(archivePath);","sourceCodeStart":508,"sourceCodeEnd":544,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/ArchiveService.cs#L508-L544","documentation":"Thrown by ArchiveService.ExtractArchive (line 526) when directoryService.FileSystem.File.Exists(archivePath) is false at extraction time. Subtle: IsValidArchive (called at line 519) already returns false — and causes an early return — when System.IO.File.Exists is false, so reaching line 523 implies the file existed moments earlier. This branch is therefore mostly a TOCTOU (file deleted between the two checks) or a divergence between System.IO.File (used in IsValidArchive) and the abstracted directoryService.FileSystem (used here).","triggerScenarios":"ExtractArchive called on a file that is deleted between IsValidArchive and the second existence check; or when directoryService.FileSystem is a different/abstraction whose existence semantics differ from System.IO.File. Used by ReadingItemService.Extract for archive (manga) reading and by CacheService extraction.","commonSituations":"Library rescan / file move deleting/replacing the archive mid-extraction; a custom IFileSystem in tests that disagrees with the real disk; network-mounted library where the file temporarily vanishes.","solutions":["Confirm the archive still exists at archivePath right before calling ExtractArchive (re-resolve the path from the DB).","Ensure the same filesystem abstraction is used for the existence check and the extraction.","Rescan the library to refresh file paths if archives were moved on disk."],"exampleFix":"// before — two different existence checks (System.IO.File vs directoryService.FileSystem)\nif (!directoryService.FileSystem.File.Exists(archivePath)) {\n    logger.LogError(\"{Archive} does not exist on disk\", archivePath);\n    throw new KavitaException($\"{archivePath} does not exist on disk\");\n}\n\n// after — resolve fresh path and use one consistent filesystem\nif (!directoryService.FileSystem.File.Exists(archivePath)) {\n    logger.LogError(\"{Archive} does not exist on disk\", archivePath);\n    throw new KavitaException($\"{archivePath} does not exist on disk\");\n}","handlingStrategy":"validation","validationCode":"// Re-resolve and check existence right before extraction using the same FS abstraction\nif (!directoryService.FileSystem.File.Exists(archivePath))\n    return; // or skip / 404 the reading request","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use one consistent filesystem abstraction for both the IsValidArchive check and ExtractArchive.","Re-resolve archive paths from the DB immediately before extraction.","Rescan the library if archives were moved/deleted on disk."],"tags":["archive","filesystem","io-error","toctou","extraction"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}