JosefNemec/Playnite · error · Exception

Cannot package extension, ID is missing!

Error message

Cannot package extension, ID is missing!

What it means

Thrown by Extensions.PackageExtension after the manifest is loaded but its Id field is null or empty. ExtensionInstaller.GetExtensionManifest(manifestPath) deserializes extension.yaml into an ExtensionManifest; if the Id property is absent or blank in the YAML, packaging stops because the packed filename (Id_Version.pext) and add-on identity both depend on it. Note this runs before VerifyManifest, so a bad Version alone will not reach here.

Source

Thrown at source/Tools/Playnite.Toolbox/Extensions.cs:195

            File.Move(Path.Combine(outDir, baseProjectName + ".sln"), outSolutionFile);
            FileSystem.ReplaceStringInFile(outProjectFile, baseProjectName, normalizedName);
            FileSystem.ReplaceStringInFile(outSolutionFile, baseProjectName, normalizedName);
            return outDir;
        }

        public static string PackageExtension(string extDirectory, string targetPath)
        {
            var dirInfo = new DirectoryInfo(extDirectory);
            var manifestPath = Path.Combine(extDirectory, PlaynitePaths.ExtensionManifestFileName);
            if (!File.Exists(manifestPath))
            {
                throw new Exception($"Manifest file ({PlaynitePaths.ExtensionManifestFileName}) not found!");
            }

            var extInfo = ExtensionInstaller.GetExtensionManifest(manifestPath);
            if (extInfo.Id.IsNullOrEmpty())
            {
                throw new Exception("Cannot package extension, ID is missing!");
            }

            extInfo.VerifyManifest();

            var packedPath = Path.Combine(targetPath, $"{Common.Paths.GetSafePathName(extInfo.Id).Replace(' ', '_')}_{extInfo.Version.ToString().Replace(".", "_")}{PlaynitePaths.PackedExtensionFileExtention}");
            FileSystem.PrepareSaveFile(packedPath);
            var ignoreFiles = File.ReadAllLines(Paths.ExtFileIgnoreListPath);

            using (var zipStream = new FileStream(packedPath, FileMode.Create))
            {
                using (var zipFile = new ZipArchive(zipStream, ZipArchiveMode.Create))
                {
                    foreach (var file in Directory.GetFiles(extDirectory, "*.*", SearchOption.AllDirectories))
                    {
                        var subName = file.Replace(extDirectory, "").TrimStart(Path.DirectorySeparatorChar);
                        if (ignoreFiles.ContainsString(subName, StringComparison.OrdinalIgnoreCase))
                        {
                            continue;

View on GitHub (pinned to 5911f4e964)

Solutions

  1. Open extension.yaml and confirm a top-level `Id: <unique_identifier>` line exists with correct two-space YAML indentation and no surrounding quotes that collapse to empty.
  2. Use the exact casing `Id` (PascalCase) — the manifest model property is `Id`, and YamlDotNet is case-sensitive here.
  3. Run a quick parse check: deserialize via ExtensionInstaller.GetExtensionManifest and assert `!string.IsNullOrEmpty(manifest.Id)` before invoking PackageExtension.
  4. Ensure Id is non-empty after any GenerateScriptExtension/GeneratePluginExtension run that may have left a placeholder.

Example fix

// before
Id:
Name: My Plugin

// after
Id: MyUniquePlugin_7c3f
Name: My Plugin
Defensive patterns

Strategy: validation

Validate before calling

var man = ExtensionInstaller.GetExtensionManifest(manifestPath);
if (man.Id.IsNullOrEmpty())
    throw new InvalidOperationException($"extension.yaml at {manifestPath} has no Id");

Prevention

When it happens

Trigger: extension.yaml exists and parses, but the top-level `Id:` key is missing, commented out, or set to an empty string. Also occurs if YAML indentation put Id under the wrong node so the deserializer never binds it.

Common situations: Authors copy a template and forget to fill in Id; an editor auto-removed a perceived-duplicate key; YAML uses a tab where a space is required, silently mis-indenting Id; Id is spelled `id:` or `ID:` and the YamlDotNet binding is case-sensitive.

Related errors


AI-assisted analysis of JosefNemec/Playnite@5911f4e964 (2026-08-13). Data as JSON: /api/errors/5fa29c468664eca4. Report an issue: GitHub.