JosefNemec/Playnite · error · Exception
Cannot package theme, ID is missing!
Error message
Cannot package theme, ID is missing!
What it means
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.
Source
Thrown at source/Tools/Playnite.Toolbox/Themes.cs:183
else
{
return FileSystem.GetMD5(file1) == FileSystem.GetMD5(file2);
}
}
public static string PackageTheme(string themeDirectory, string targetPath, ApplicationMode mode)
{
var dirInfo = new DirectoryInfo(themeDirectory);
var manifestPath = Path.Combine(themeDirectory, PlaynitePaths.ThemeManifestFileName);
if (!File.Exists(manifestPath))
{
throw new Exception($"Manifest file ({PlaynitePaths.ThemeManifestFileName}) not found!");
}
var extInfo = ExtensionInstaller.GetThemeManifest(manifestPath);
if (extInfo.Id.IsNullOrEmpty())
{
throw new Exception("Cannot package theme, ID is missing!");
}
extInfo.VerifyManifest();
var apiVer = extInfo.Mode == ApplicationMode.Desktop ? ThemeManager.DesktopApiVersion : ThemeManager.FullscreenApiVersion;
var themeApiVer = Version.Parse(extInfo.ThemeApiVersion);
if (themeApiVer > apiVer)
{
throw new Exception($"Cannot package theme. Unsupported API version detected: {themeApiVer}");
}
else if (themeApiVer != apiVer)
{
logger.Warn("Selected theme has not been updated to the latest supported API version. Please consider updating the theme!");
logger.Warn("https://github.com/JosefNemec/Playnite/issues/1259");
}
var defaultThemeDir = Path.Combine(Paths.GetThemesPath(mode), "Default");
targetPath = Path.Combine(targetPath, $"{Common.Paths.GetSafePathName(extInfo.Id).Replace(' ', '_')}_{extInfo.Version.ToString().Replace(".", "_")}{PlaynitePaths.PackedThemeFileExtention}");
View on GitHub (pinned to 5911f4e964)
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.
Example fix
// before Id: Name: My Theme // after Id: MyTheme_a1b2 Name: My Theme
Defensive patterns
Strategy: validation
Validate before calling
var man = ExtensionInstaller.GetThemeManifest(manifestPath);
if (man.Id.IsNullOrEmpty())
throw new InvalidOperationException($"theme.yaml at {manifestPath} has no Id"); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot package extension, ID is missing!
- LOC.GeneralThemePackageError
- Manifest file ({PlaynitePaths.ThemeManifestFileName}) not fo
- LOC.GeneralExtensionPackageError
- Manifest file ({PlaynitePaths.ExtensionManifestFileName}) no
AI-assisted analysis of JosefNemec/Playnite@5911f4e964 (2026-08-13).
Data as JSON: /api/errors/a1f78591529318f1.
Report an issue: GitHub.