{"record":{"id":"886f3b6e9367aea4","repo":"JosefNemec/Playnite","slug":"backup-output-path-not-specified","errorCode":null,"errorMessage":"Backup output path not specified!","messagePattern":"Backup output path not specified!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"source/Playnite/Backup.cs","lineNumber":77,"sourceCode":"        private const string autoBackupFilePattern = autoBackupFileName + @\"\\-\\d{4}\\-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{2}\";\r\n        private const string autoBackupFileName = \"PlayniteBackup\";\r\n        private const string libraryEntryRoot = \"library\";\r\n        private const string libraryFilesEntryRoot = \"libraryfiles\";\r\n        private const string extensionsDataEntryRoot = \"extensiondata\";\r\n        private const string extensionsEntryRoot = \"extension\";\r\n        private const string themesEntryRoot = \"themes\";\r\n        private static readonly string[] configFilesNames = new string[] { PlaynitePaths.ConfigFileName, PlaynitePaths.FullscreenConfigFileName };\r\n\r\n        public static void BackupData(string optionsFile, CancellationToken cancelToken)\r\n        {\r\n            BackupData(Serialization.FromJsonFile<BackupOptions>(optionsFile), cancelToken);\r\n        }\r\n\r\n        public static void BackupData(BackupOptions options, CancellationToken cancelToken)\r\n        {\r\n            if (options.OutputDir.IsNullOrEmpty() && options.OutputFile.IsNullOrEmpty())\r\n            {\r\n                throw new Exception(\"Backup output path not specified!\");\r\n            }\r\n\r\n            if (!options.OutputDir.IsNullOrEmpty())\r\n            {\r\n                options.OutputFile = Path.Combine(options.OutputDir, GetAutoBackupFileName());\r\n            }\r\n\r\n            if (options.BackupItems == null)\r\n            {\r\n                options.BackupItems = new List<BackupDataItem>();\r\n            }\r\n\r\n            FileSystem.DeleteFile(options.OutputFile);\r\n            using (var zipFile = new FileStream(options.OutputFile, FileMode.Create))\r\n            using (var archive = new ZipArchive(zipFile, ZipArchiveMode.Create))\r\n            {\r\n                // Settings\r\n                foreach (var config in configFilesNames)\r","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/JosefNemec/Playnite/blob/5911f4e964e628aa7a69c2030ab35101afa63067/source/Playnite/Backup.cs#L59-L95","documentation":"Thrown by BackupData when neither OutputDir nor OutputFile is set on the supplied BackupOptions. The backup logic needs a concrete destination; with both null/empty it has nowhere to write the archive, so it aborts before doing any work.","triggerScenarios":"Calling BackupData(options, cancelToken) where options.OutputDir and options.OutputFile are both null or empty strings (IsNullOrEmpty returns true for each).","commonSituations":"A config file passed to BackupData(optionsFile, ...) omits the output fields; CLI/​script invokes the backup with no destination flag; a serialized BackupOptions round-trip dropped the destination; typo in the JSON key name.","solutions":["Set options.OutputDir to a valid directory (a timestamped filename is generated from it).","Alternatively set options.OutputFile to an explicit full archive path.","If loading options from JSON, validate the keys exist and are non-empty before calling BackupData.","For CLI usage, pass the output path argument and confirm it is bound to the options."],"exampleFix":"// before\nvar opts = new BackupOptions { BackupItems = items };\nBackup.BackupData(opts, token);\n\n// after\nvar opts = new BackupOptions { OutputDir = @\"D:\\Backups\\Playnite\", BackupItems = items };\nif (opts.OutputDir.IsNullOrEmpty() && opts.OutputFile.IsNullOrEmpty())\n    throw new InvalidOperationException(\"Backup destination required.\");\nBackup.BackupData(opts, token);","handlingStrategy":"validation","validationCode":"if (options.OutputDir.IsNullOrEmpty() && options.OutputFile.IsNullOrEmpty())\n{\n    throw new InvalidOperationException(\"Specify BackupOptions.OutputDir or OutputFile.\");\n}\nBackup.BackupData(options, cancelToken);","typeGuard":null,"tryCatchPattern":"try { Backup.BackupData(options, cancelToken); }\ncatch (Exception e) when (e.Message.Contains(\"output path\", StringComparison.OrdinalIgnoreCase))\n{ logger.Error(e, \"Backup destination missing.\"); }","preventionTips":["Always set OutputDir or OutputFile on BackupOptions.","Validate options JSON keys before deserializing into BackupData(optionsFile).","Unit-test the backup path with a known destination."],"tags":["backup","configuration","validation","playnite"],"backgroundTag":null,"analyzedSha":"5911f4e964e628aa7a69c2030ab35101afa63067","analyzedAt":"2026-08-13T17:38:25.713Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}