JosefNemec/Playnite · error · LocalizedException
LOC.GeneralThemePackageError
Error message
LOC.GeneralThemePackageError
What it means
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.
Source
Thrown at source/Playnite/Plugins/ExtensionInstaller.cs:373
}
if (zip.Entries.Any(a => a.Name == "Playnite.dll" || a.Name == "Playnite.Common.dll" || a.Name == "Playnite.SDK.dll"))
{
logger.Error($"Extension package is invalid, includes not allowed Playnite dependencies.");
throw new LocalizedException(LOC.GeneralExtensionPackageError);
}
}
}
public static void VerifyThemePackage(string packagePath)
{
using (var zip = ZipFile.OpenRead(packagePath))
{
var manifestEntry = zip.GetEntry(PlaynitePaths.ThemeManifestFileName);
if (manifestEntry == null)
{
logger.Error("Theme package is invalid, no manifest found.");
throw new LocalizedException(LOC.GeneralThemePackageError);
}
using (var logStream = manifestEntry.Open())
{
using (TextReader tr = new StreamReader(logStream))
{
var manifest = Serialization.FromYaml<ThemeManifest>(tr.ReadToEnd());
if (manifest.Id.IsNullOrEmpty())
{
logger.Error("Theme package is invalid, no extension ID found.");
throw new LocalizedException(LOC.GeneralThemePackageError);
}
if (!Version.TryParse(manifest.Version, out var _))
{
logger.Error($"Theme package is invalid, version is not in correct format {manifest.Version}.");
throw new LocalizedException(LOC.GeneralThemePackageError);
}
View on GitHub (pinned to 5911f4e964)
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.
Example fix
# before — zip layout MyTheme/theme.yaml MyTheme/App.xaml # after theme.yaml App.xaml
Defensive patterns
Strategy: validation
Validate before calling
using (var zip = ZipFile.OpenRead(packagePath))
{
if (zip.GetEntry(PlaynitePaths.ThemeManifestFileName) == null)
throw new InvalidOperationException($"Theme package missing {PlaynitePaths.ThemeManifestFileName} at root.");
} Type guard
static bool ThemeManifestAtRoot(string packagePath)
{
using (var z = ZipFile.OpenRead(packagePath))
return z.GetEntry(PlaynitePaths.ThemeManifestFileName) != null;
} Try / catch
try { ExtensionInstaller.VerifyThemePackage(path); }
catch (LocalizedException) { /* open zip, ensure theme.yaml at archive root */ } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot package extension, ID is missing!
- Cannot package theme, ID is missing!
- LOC.GeneralExtensionPackageError
- Theme/Extension is using unsupported API version.
- Failed to download update manifest.
AI-assisted analysis of JosefNemec/Playnite@5911f4e964 (2026-08-13).
Data as JSON: /api/errors/05fc609be4f35915.
Report an issue: GitHub.