{"record":{"id":"76d5d6b4f18f5888","repo":"microsoft/aspire","slug":"the-aspire-skills-bundle-staging-directory-must-have-a-76d5d6","errorCode":null,"errorMessage":"The Aspire skills bundle staging directory must have a parent directory.","messagePattern":"The Aspire skills bundle staging directory must have a parent directory\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Agents/AspireSkills/EmbeddedAspireSkillsBundleProvider.cs","lineNumber":71,"sourceCode":"        CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(bundleDirectory);\n\n        var metadata = Metadata;\n        if (metadata is null || string.IsNullOrWhiteSpace(metadata.Sha512))\n        {\n            return null;\n        }\n\n        await using var archiveStream = OpenArchive();\n        if (archiveStream is null)\n        {\n            return null;\n        }\n\n        Directory.CreateDirectory(bundleDirectory.FullName);\n        var temporaryDirectoryRoot = bundleDirectory.Parent\n            ?? throw new InvalidOperationException(\"The Aspire skills bundle staging directory must have a parent directory.\");\n        // Keep the archive beside the staging directory so a transient Windows file lock during\n        // best-effort cleanup cannot prevent the validated staging directory from being published.\n        using var temporaryDirectory = TemporaryCacheDirectory.Create(\n            temporaryDirectoryRoot.FullName,\n            \"embedded\",\n            path => FileDeleteHelper.TryDeleteDirectory(path),\n            path => FileDeleteHelper.TryDeleteFile(path));\n        var archivePath = Path.Combine(temporaryDirectory.FullName, \"bundle.tgz\");\n\n        await using (var fileStream = File.Create(archivePath))\n        {\n            await archiveStream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);\n        }\n\n        return await _bundleProvider.CreateAsync(\n            new FileInfo(archivePath),\n            bundleDirectory,\n            metadata.Sha512,","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Agents/AspireSkills/EmbeddedAspireSkillsBundleProvider.cs#L53-L89","documentation":"EmbeddedAspireSkillsBundleProvider.CreateBundleAsync creates a staging directory for the embedded skills bundle and needs its parent to host temporary files. This guard throws when bundleDirectory.Parent is null, i.e. the staging path is a filesystem root (or otherwise has no parent). It is an internal invariant check — normal CLI usage never passes a root path here.","triggerScenarios":"Thrown in CreateBundleAsync when the resolved bundle staging directory (bundleDirectory) has no Parent — e.g. the temp/cache root resolved to a drive root like C:\\ or /, typically because an environment override (TMP/TEMP or a cache-path setting) points at a root directory.","commonSituations":"TMP or TEMP environment variable set to a drive root on Windows; a custom ASPIRE cache/home override pointing at \"/\"; exotic sandbox/container setups with unusual temp paths.","solutions":["Check and fix the TMP/TEMP environment variables (or the Aspire cache-path override) so they point to a real subdirectory, not a drive/filesystem root.","Point the override at e.g. %TEMP%\\aspire or /tmp/aspire and re-run the command.","If running in a container/sandbox, ensure a writable non-root temp directory is mounted and referenced."],"exampleFix":"// before\nset TEMP=C:\\\n// after\nset TEMP=C:\\Users\\me\\AppData\\Local\\Temp","handlingStrategy":"validation","validationCode":"if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(\"TMP\")) ||\n    Path.GetPathRoot(Path.GetFullPath(Environment.GetEnvironmentVariable(\"TMP\")!)) == Path.GetFullPath(Environment.GetEnvironmentVariable(\"TMP\")!))\n{\n    // fix TEMP/TMP to a non-root directory before running the CLI\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never point TMP/TEMP or Aspire cache-path overrides at a drive/filesystem root.","In containers, mount a writable temp subdirectory rather than using '/'.","Verify cache-path overrides resolve to a path with a parent directory."],"tags":["filesystem","path","temp-directory","cli"],"backgroundTag":"null-argument","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"}