{"record":{"id":"4787954196a3ad28","repo":"JosefNemec/Playnite","slug":"cannot-add-file-to-database-file-not-found","errorCode":null,"errorMessage":"Cannot add file to database, file not found.","messagePattern":"Cannot add file to database, file not found\\.","errorType":"exception","errorClass":"FileNotFoundException","httpStatus":null,"severity":"error","filePath":"source/Playnite/API/DatabaseAPI.cs","lineNumber":60,"sourceCode":"        {\r\n            get => database?.DatabasePath;\r\n        }\r\n\r\n        public bool IsOpen\r\n        {\r\n            get => database?.IsOpen == true;\r\n        }\r\n\r\n        public DatabaseAPI(GameDatabase database)\r\n        {\r\n            this.database = database;\r\n        }\r\n\r\n        public string AddFile(string path, Guid parentId)\r\n        {\r\n            if (!File.Exists(path))\r\n            {\r\n                throw new FileNotFoundException(\"Cannot add file to database, file not found.\");\r\n            }\r\n\r\n            return database.AddFile(path, parentId, false, CancellationToken.None);\r\n        }\r\n\r\n        public void SaveFile(string id, string path)\r\n        {\r\n            database.CopyFile(id, path);\r\n        }\r\n\r\n        public void RemoveFile(string id)\r\n        {\r\n            database.RemoveFile(id);\r\n        }\r\n\r\n        public IDisposable BufferedUpdate()\r\n        {\r\n            return database.BufferedUpdate();\r","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/JosefNemec/Playnite/blob/5911f4e964e628aa7a69c2030ab35101afa63067/source/Playnite/API/DatabaseAPI.cs#L42-L78","documentation":"Thrown by DatabaseAPI.AddFile when the local file referenced by `path` does not exist on disk before the database attempts to import it. Playnite refuses to add a database entry for a missing file because the GameDatabase.AddFile call downstream would read/​copy bytes that are not there. The check is an explicit File.Exists guard at the API boundary, distinct from the lower-level database read failures.","triggerScenarios":"Calling api.Database.AddFile(path, parentId) where `path` is relative, points to a temp file already moved/deleted, a download target that hasn't finished writing, or a path with the wrong casing/separator on the current OS. Any string for which File.Exists returns false trips it.","commonSituations":"Extension code that downloads cover art to a temp dir and adds it before the download stream closes; passing a URL or a database file id instead of a real filesystem path; running on a path that exceeds MAX_PATH before Paths.FixPathLength is applied; antivirus quarantining the file between creation and AddFile.","solutions":["Verify File.Exists(path) immediately before calling AddFile and skip/log if false.","Resolve `path` to an absolute path with Path.GetFullPath and confirm the directory exists first.","If the file comes from a download or copy, await/flush that operation to completion before AddFile.","Re-check the path after long-running work, since the file may have been removed by AV or cleanup."],"exampleFix":"// before\ndb.AddFile(tempCover, game.Id);\n\n// after\nif (!File.Exists(tempCover))\n{\n    logger.Warn($\"Cover missing, skipping: {tempCover}\");\n    return;\n}\ndb.AddFile(tempCover, game.Id);","handlingStrategy":"validation","validationCode":"if (!File.Exists(Path.GetFullPath(path)))\n{\n    return; // or log and skip\n}\nvar id = api.Database.AddFile(path, parentId);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always File.Exists-check at the boundary before AddFile.","Resolve to absolute paths with Path.GetFullPath before checking.","Await downloads/copies to completion before importing.","Re-check existence after any long-running prior step."],"tags":["filesystem","database","api-validation","playnite"],"backgroundTag":null,"analyzedSha":"5911f4e964e628aa7a69c2030ab35101afa63067","analyzedAt":"2026-08-13T17:38:25.713Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}