{"record":{"id":"a1f78591529318f1","repo":"JosefNemec/Playnite","slug":"cannot-package-theme-id-is-missing","errorCode":null,"errorMessage":"Cannot package theme, ID is missing!","messagePattern":"Cannot package theme, ID is missing!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"source/Tools/Playnite.Toolbox/Themes.cs","lineNumber":183,"sourceCode":"            else\r\n            {\r\n                return FileSystem.GetMD5(file1) == FileSystem.GetMD5(file2);\r\n            }\r\n        }\r\n\r\n        public static string PackageTheme(string themeDirectory, string targetPath, ApplicationMode mode)\r\n        {\r\n            var dirInfo = new DirectoryInfo(themeDirectory);\r\n            var manifestPath = Path.Combine(themeDirectory, PlaynitePaths.ThemeManifestFileName);\r\n            if (!File.Exists(manifestPath))\r\n            {\r\n                throw new Exception($\"Manifest file ({PlaynitePaths.ThemeManifestFileName}) not found!\");\r\n            }\r\n\r\n            var extInfo = ExtensionInstaller.GetThemeManifest(manifestPath);\r\n            if (extInfo.Id.IsNullOrEmpty())\r\n            {\r\n                throw new Exception(\"Cannot package theme, ID is missing!\");\r\n            }\r\n\r\n            extInfo.VerifyManifest();\r\n\r\n            var apiVer = extInfo.Mode == ApplicationMode.Desktop ? ThemeManager.DesktopApiVersion : ThemeManager.FullscreenApiVersion;\r\n            var themeApiVer = Version.Parse(extInfo.ThemeApiVersion);\r\n            if (themeApiVer > apiVer)\r\n            {\r\n                throw new Exception($\"Cannot package theme. Unsupported API version detected: {themeApiVer}\");\r\n            }\r\n            else if (themeApiVer != apiVer)\r\n            {\r\n                logger.Warn(\"Selected theme has not been updated to the latest supported API version. Please consider updating the theme!\");\r\n                logger.Warn(\"https://github.com/JosefNemec/Playnite/issues/1259\");\r\n            }\r\n\r\n            var defaultThemeDir = Path.Combine(Paths.GetThemesPath(mode), \"Default\");\r\n            targetPath = Path.Combine(targetPath, $\"{Common.Paths.GetSafePathName(extInfo.Id).Replace(' ', '_')}_{extInfo.Version.ToString().Replace(\".\", \"_\")}{PlaynitePaths.PackedThemeFileExtention}\");\r","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/JosefNemec/Playnite/blob/5911f4e964e628aa7a69c2030ab35101afa63067/source/Tools/Playnite.Toolbox/Themes.cs#L165-L201","documentation":"Thrown by Themes.PackageTheme after GetThemeManifest succeeds but the Id field is null or empty. The theme's packed filename (Id_Version.pthm) and identity require Id, so packaging halts. This check precedes VerifyManifest and the API-version comparison.","triggerScenarios":"theme.yaml parses but its top-level `Id:` is missing, blank, or mis-indented so YamlDotNet does not bind it. YamlDotNet binding is case-sensitive, so `id:` or `ID:` also leaves the property empty.","commonSituations":"Copying a Default theme and forgetting to set a unique Id; an editor stripping perceived-blank lines; tabs instead of spaces breaking YAML structure; placeholder Id left empty from the template.","solutions":["Add a non-empty top-level `Id: <unique_theme_id>` to theme.yaml with correct two-space indentation.","Match the exact casing `Id` (the manifest model property name).","Pre-validate by deserializing via ExtensionInstaller.GetThemeManifest and asserting `!string.IsNullOrEmpty(man.Id)`.","Give the theme a unique Id to avoid collisions with the Default or other installed themes."],"exampleFix":"// before\nId:\nName: My Theme\n\n// after\nId: MyTheme_a1b2\nName: My Theme","handlingStrategy":"validation","validationCode":"var man = ExtensionInstaller.GetThemeManifest(manifestPath);\nif (man.Id.IsNullOrEmpty())\n    throw new InvalidOperationException($\"theme.yaml at {manifestPath} has no Id\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the exact key `Id` (PascalCase) in theme.yaml with two-space indentation.","Deserialize and assert a non-empty Id before packaging.","Give every theme a unique Id to avoid install collisions."],"tags":["themes","packaging","manifest","toolbox","validation","yaml"],"backgroundTag":null,"analyzedSha":"5911f4e964e628aa7a69c2030ab35101afa63067","analyzedAt":"2026-08-13T17:38:25.713Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}