{"record":{"id":"b7673cee64ae8966","repo":"microsoft/aspire","slug":"aspire-skills-bundle-contains-an-empty-relative-path","errorCode":null,"errorMessage":"Aspire skills bundle contains an empty relative path.","messagePattern":"Aspire skills bundle contains an empty relative path\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Agents/AspireSkills/AspireSkillsBundleProvider.cs","lineNumber":405,"sourceCode":"            {\n                continue;\n            }\n\n            var value = line[keyPrefix.Length..].Trim();\n            return value.Length >= 2 &&\n                   ((value[0] == '\"' && value[^1] == '\"') || (value[0] == '\\'' && value[^1] == '\\''))\n                ? value[1..^1]\n                : value;\n        }\n\n        return null;\n    }\n\n    internal static string NormalizeRelativePath(string? relativePath)\n    {\n        if (string.IsNullOrWhiteSpace(relativePath))\n        {\n            throw new InvalidOperationException(\"Aspire skills bundle contains an empty relative path.\");\n        }\n\n        var normalizedPath = relativePath\n            .Replace('/', Path.DirectorySeparatorChar)\n            .Replace('\\\\', Path.DirectorySeparatorChar);\n\n        if (Path.IsPathRooted(normalizedPath))\n        {\n            throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, \"Aspire skills bundle path '{0}' must be relative.\", relativePath));\n        }\n\n        var segments = normalizedPath.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);\n        if (segments.Length == 0 || segments.Any(static segment => !IsPortablePathSegment(segment)))\n        {\n            throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, \"Aspire skills bundle path '{0}' is not safe.\", relativePath));\n        }\n\n        return Path.Combine(segments);","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Agents/AspireSkills/AspireSkillsBundleProvider.cs#L387-L423","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the bundle so every path entry is a non-empty relative path such as \"skills/my-skill/SKILL.md\"","Remove empty/null entries from skill-manifest.json","Regenerate or re-download the bundle if it was produced by a tool"],"exampleFix":"// before (skill-manifest.json)\n{ \"files\": [ { \"path\": \"\" } ] }\n// after\n{ \"files\": [ { \"path\": \"skills/my-skill/SKILL.md\" } ] }","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(entry.Path))\n    throw new Exception(\"Bundle manifest contains an empty relative path.\");","typeGuard":null,"tryCatchPattern":"try { await provider.CreateAsync(...); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"empty relative path\")) { /* fix manifest entry */ }","preventionTips":["Validate skill-manifest.json against a schema requiring non-empty path strings","Remove placeholder entries instead of leaving them empty","Run bundle validation before publishing an archive"],"tags":["path","validation","skills","cli"],"backgroundTag":"empty-required-field","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"}