{"record":{"id":"d1f4a00c45ec3de9","repo":"microsoft/aspire","slug":"downloaded-aspire-skills-package-contains-multiple-skill","errorCode":null,"errorMessage":"Downloaded Aspire skills package contains multiple skill-manifest.json files.","messagePattern":"Downloaded Aspire skills package contains multiple skill-manifest\\.json files\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Agents/AspireSkills/AspireSkillsBundleProvider.cs","lineNumber":602,"sourceCode":"        var packageManifestPath = Path.Combine(packageDirectory, ManifestFileName);\n        if (File.Exists(packageManifestPath))\n        {\n            return new DirectoryInfo(packageDirectory);\n        }\n\n        var topLevelBundleDirectories = Directory\n            .EnumerateDirectories(extractionDirectory)\n            .Where(directory => File.Exists(Path.Combine(directory, ManifestFileName)))\n            .ToArray();\n\n        if (topLevelBundleDirectories.Length == 1)\n        {\n            return new DirectoryInfo(topLevelBundleDirectories[0]);\n        }\n\n        if (topLevelBundleDirectories.Length > 1)\n        {\n            throw new InvalidOperationException(\"Downloaded Aspire skills package contains multiple skill-manifest.json files.\");\n        }\n\n        throw new InvalidOperationException(\"Downloaded Aspire skills package does not contain skill-manifest.json.\");\n    }\n\n    private static void CopyDirectory(string sourceDirectory, string targetDirectory)\n    {\n        Directory.CreateDirectory(targetDirectory);\n\n        foreach (var sourceFile in Directory.GetFiles(sourceDirectory, \"*\", SearchOption.AllDirectories))\n        {\n            var relativePath = Path.GetRelativePath(sourceDirectory, sourceFile);\n            var targetFile = Path.Combine(targetDirectory, relativePath);\n            var targetFileDirectory = Path.GetDirectoryName(targetFile);\n            if (!string.IsNullOrEmpty(targetFileDirectory))\n            {\n                Directory.CreateDirectory(targetFileDirectory);\n            }","sourceCodeStart":584,"sourceCodeEnd":620,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Agents/AspireSkills/AspireSkillsBundleProvider.cs#L584-L620","documentation":"FindBundleRoot scans an extracted skills package for top-level directories containing skill-manifest.json. Exactly one bundle root is expected; discovering two or more means the archive packs multiple bundles ambiguously, so Aspire throws this InvalidOperationException rather than guessing which to install.","triggerScenarios":"AspireSkillsBundleProvider.CreateAsync extracts a downloaded package, and FindBundleRoot finds more than one top-level directory each containing a skill-manifest.json.","commonSituations":"Manually merging several bundles into one archive; a packaging script zipping multiple skill folders that each have their own manifest; accidental duplicate manifest placement during repackaging.","solutions":["Repackage so only one top-level directory contains skill-manifest.json (nested skills belong under that single bundle root)","Remove the extra skill-manifest.json files or split the package into separate bundles","Re-download the official package instead of a hand-merged copy"],"exampleFix":"// before (archive layout)\nbundleA/skill-manifest.json\nbundleB/skill-manifest.json\n// after\nbundle/skill-manifest.json\nbundle/skills/bundleA/...\nbundle/skills/bundleB/...","handlingStrategy":"validation","validationCode":"var manifests = Directory.EnumerateDirectories(extractDir, \"skill-manifest.json\", SearchOption.AllDirectories).ToList();\nif (manifests.Count != 1) throw new Exception($\"Expected exactly one skill-manifest.json, found {manifests.Count}.\");","typeGuard":null,"tryCatchPattern":"try { await provider.CreateAsync(...); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"multiple skill-manifest.json\")) { /* repackage with a single bundle root */ }","preventionTips":["Package exactly one bundle per archive","Keep a single skill-manifest.json at the bundle root only","Add a packaging CI check that counts manifests in the output archive"],"tags":["skills","archive","manifest","validation"],"backgroundTag":"unexpected-response-shape","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}