{"record":{"id":"05fc609be4f35915","repo":"JosefNemec/Playnite","slug":"loc-generalthemepackageerror","errorCode":null,"errorMessage":"LOC.GeneralThemePackageError","messagePattern":"LOC\\.GeneralThemePackageError","errorType":"exception","errorClass":"LocalizedException","httpStatus":null,"severity":"error","filePath":"source/Playnite/Plugins/ExtensionInstaller.cs","lineNumber":373,"sourceCode":"                }\r\n\r\n                if (zip.Entries.Any(a => a.Name == \"Playnite.dll\" || a.Name == \"Playnite.Common.dll\" || a.Name == \"Playnite.SDK.dll\"))\r\n                {\r\n                    logger.Error($\"Extension package is invalid, includes not allowed Playnite dependencies.\");\r\n                    throw new LocalizedException(LOC.GeneralExtensionPackageError);\r\n                }\r\n            }\r\n        }\r\n\r\n        public static void VerifyThemePackage(string packagePath)\r\n        {\r\n            using (var zip = ZipFile.OpenRead(packagePath))\r\n            {\r\n                var manifestEntry = zip.GetEntry(PlaynitePaths.ThemeManifestFileName);\r\n                if (manifestEntry == null)\r\n                {\r\n                    logger.Error(\"Theme package is invalid, no manifest found.\");\r\n                    throw new LocalizedException(LOC.GeneralThemePackageError);\r\n                }\r\n\r\n                using (var logStream = manifestEntry.Open())\r\n                {\r\n                    using (TextReader tr = new StreamReader(logStream))\r\n                    {\r\n                        var manifest = Serialization.FromYaml<ThemeManifest>(tr.ReadToEnd());\r\n                        if (manifest.Id.IsNullOrEmpty())\r\n                        {\r\n                            logger.Error(\"Theme package is invalid, no extension ID found.\");\r\n                            throw new LocalizedException(LOC.GeneralThemePackageError);\r\n                        }\r\n\r\n                        if (!Version.TryParse(manifest.Version, out var _))\r\n                        {\r\n                            logger.Error($\"Theme package is invalid, version is not in correct format {manifest.Version}.\");\r\n                            throw new LocalizedException(LOC.GeneralThemePackageError);\r\n                        }\r","sourceCodeStart":355,"sourceCodeEnd":391,"githubUrl":"https://github.com/JosefNemec/Playnite/blob/5911f4e964e628aa7a69c2030ab35101afa63067/source/Playnite/Plugins/ExtensionInstaller.cs#L355-L391","documentation":"Thrown by VerifyThemePackage when a theme .pthm package has no theme manifest entry (the file named by PlaynitePaths.ThemeManifestFileName, i.e. theme.yaml). Without the manifest Playnite cannot read the theme's Id, so the package is rejected via LOC.GeneralThemePackageError.","triggerScenarios":"VerifyThemePackage opens the zip, calls zip.GetEntry(PlaynitePaths.ThemeManifestFileName), and gets null at line 370. The manifest file is absent, misnamed, or placed in a subfolder so GetEntry (which is path-sensitive) returns null.","commonSituations":"Author zipped the project folder rather than its contents, so theme.yaml landed under a nested directory. File named 'theme.yml' instead of 'theme.yaml', or 'Theme.yaml' with wrong casing on a case-sensitive zip. Build script skipped copying the manifest.","solutions":["Unzip the .pthm and confirm theme.yaml sits at the archive root, not inside a subfolder.","Verify the exact filename and casing match PlaynitePaths.ThemeManifestFileName (theme.yaml).","Re-zip the directory contents (not the parent folder) so entries are top-level.","Re-run VerifyThemePackage before publishing."],"exampleFix":"# before — zip layout\nMyTheme/theme.yaml\nMyTheme/App.xaml\n\n# after\ntheme.yaml\nApp.xaml","handlingStrategy":"validation","validationCode":"using (var zip = ZipFile.OpenRead(packagePath))\n{\n    if (zip.GetEntry(PlaynitePaths.ThemeManifestFileName) == null)\n        throw new InvalidOperationException($\"Theme package missing {PlaynitePaths.ThemeManifestFileName} at root.\");\n}","typeGuard":"static bool ThemeManifestAtRoot(string packagePath)\n{\n    using (var z = ZipFile.OpenRead(packagePath))\n        return z.GetEntry(PlaynitePaths.ThemeManifestFileName) != null;\n}","tryCatchPattern":"try { ExtensionInstaller.VerifyThemePackage(path); }\ncatch (LocalizedException) { /* open zip, ensure theme.yaml at archive root */ }","preventionTips":["Zip the directory contents, not the parent folder, so entries sit at the root.","Confirm exact filename casing (theme.yaml).","Add a CI check that lists zip entries and asserts theme.yaml is present at root."],"tags":["theme","manifest","yaml","packaging","validation","playnite"],"backgroundTag":null,"analyzedSha":"5911f4e964e628aa7a69c2030ab35101afa63067","analyzedAt":"2026-08-13T17:38:25.713Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}