microsoft/aspire · error · InvalidOperationException

Aspire skills bundle contains an empty relative path.

Error message

Aspire skills bundle contains an empty relative path.

What it means

NormalizeRelativePath validates every path entry in a skills bundle (manifest entries, file copies). A null/whitespace path cannot address any file, so Aspire throws this InvalidOperationException to reject the bundle early instead of failing later with a confusing filesystem error.

Solutions

  1. Fix the bundle so every path entry is a non-empty relative path such as "skills/my-skill/SKILL.md"
  2. Remove empty/null entries from skill-manifest.json
  3. Regenerate or re-download the bundle if it was produced by a tool

Example fix

// before (skill-manifest.json)
{ "files": [ { "path": "" } ] }
// after
{ "files": [ { "path": "skills/my-skill/SKILL.md" } ] }
Defensive patterns

Strategy: validation

Validate before calling

if (string.IsNullOrWhiteSpace(entry.Path))
    throw new Exception("Bundle manifest contains an empty relative path.");

Try / catch

try { await provider.CreateAsync(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("empty relative path")) { /* fix manifest entry */ }

Prevention

When it happens

Trigger: A bundle manifest (skill-manifest.json) lists a file entry with an empty string, whitespace-only string, or null relativePath, and the bundle is processed through AspireSkillsBundleProvider.CreateAsync.

Common situations: Hand-edited manifest JSON with a trailing empty entry; a code generator emitting empty paths; JSON fields left as "" when a value was meant to be filled in.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/b7673cee64ae8966. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Cli/Agents/AspireSkills/AspireSkillsBundleProvider.cs:405

            {
                continue;
            }

            var value = line[keyPrefix.Length..].Trim();
            return value.Length >= 2 &&
                   ((value[0] == '"' && value[^1] == '"') || (value[0] == '\'' && value[^1] == '\''))
                ? value[1..^1]
                : value;
        }

        return null;
    }

    internal static string NormalizeRelativePath(string? relativePath)
    {
        if (string.IsNullOrWhiteSpace(relativePath))
        {
            throw new InvalidOperationException("Aspire skills bundle contains an empty relative path.");
        }

        var normalizedPath = relativePath
            .Replace('/', Path.DirectorySeparatorChar)
            .Replace('\\', Path.DirectorySeparatorChar);

        if (Path.IsPathRooted(normalizedPath))
        {
            throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Aspire skills bundle path '{0}' must be relative.", relativePath));
        }

        var segments = normalizedPath.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
        if (segments.Length == 0 || segments.Any(static segment => !IsPortablePathSegment(segment)))
        {
            throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Aspire skills bundle path '{0}' is not safe.", relativePath));
        }

        return Path.Combine(segments);

View on GitHub (pinned to 25830f84bd)