{"record":{"id":"4b3873c9cc45382c","repo":"iOfficeAI/OfficeCLI","slug":"path-is-empty-pass-a-relative-skill-file-e-g-r","errorCode":null,"errorMessage":"path is empty — pass a relative skill file, e.g. reference/decision-rules.md","messagePattern":"path is empty — pass a relative skill file, e\\.g\\. reference/decision-rules\\.md","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/SkillInstaller.cs","lineNumber":290,"sourceCode":"            .OrderBy(rel => rel, StringComparer.OrdinalIgnoreCase)\n            .ToList();\n    }\n\n    /// <summary>\n    /// Return the text content of one bundled reference file inside a skill\n    /// (e.g. \"reference/decision-rules.md\"). Shared by the CLI\n    /// `load_skill &lt;name&gt; --path &lt;rel&gt;` command and the MCP\n    /// `load_skill` tool's path= argument. Throws on unknown skill, path\n    /// traversal, a binary asset (cannot ride the text channel), or a missing\n    /// file.\n    /// </summary>\n    public static string LoadSkillFile(string skillName, string relativePath)\n    {\n        if (!SkillMap.TryGetValue(skillName, out var folder))\n            throw new ArgumentException($\"Unknown skill: {skillName}. Available: {KnownSkillsList()}\");\n        var rel = (relativePath ?? \"\").Replace('\\\\', '/').TrimStart('/');\n        if (rel.Length == 0)\n            throw new ArgumentException(\"path is empty — pass a relative skill file, e.g. reference/decision-rules.md\");\n        // Contain to the skill folder: reject traversal and current-dir segments.\n        if (rel.Split('/').Any(seg => seg is \"..\" or \".\"))\n            throw new ArgumentException($\"Invalid skill file path: {relativePath}\");\n        if (BinarySkillExtensions.Contains(Path.GetExtension(rel)))\n            throw new ArgumentException(\n                $\"'{rel}' is a binary asset and cannot be served over the text channel. \" +\n                $\"Install the skill to get it on disk: officecli skills install {skillName}\");\n        var content = LoadEmbeddedResource($\"skills/{folder}/{rel}\");\n        if (content == null)\n            throw new ArgumentException(\n                $\"Skill file not found: {rel}. List available files via the manifest at the end of: \" +\n                $\"officecli load_skill {skillName}\");\n        return content;\n    }\n\n    /// <summary>\n    /// Build the \"Reference files\" manifest appended to a SKILL.md. Deep trees\n    /// (≥ 3 path segments, e.g. a 52-directory style library) collapse to one","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/SkillInstaller.cs#L272-L308","documentation":"Thrown by LoadSkillFile when the relativePath, after backslash-to-slash conversion and leading-slash trimming, is empty. Guards against a caller passing null, '', '/', or whitespace-only path. ArgumentException with an explicit hint to pass something like 'reference/decision-rules.md'.","triggerScenarios":"Calling load_skill with --path omitted, empty, '/', or '\\', or an MCP path= of null/empty. The check fires after the skill-name check passes.","commonSituations":"MCP caller omits the path field, CLI user forgets the --path value, or a script passes an untrimmed variable that resolves to empty.","solutions":["Pass a non-empty relative path such as 'reference/decision-rules.md'.","Get valid paths from the manifest appended to load_skill output, or from ListSkillFiles.","Check the path variable is non-empty before calling."],"exampleFix":"// before\nofficecli load_skill morph-ppt --path \"\"\n// after\nofficecli load_skill morph-ppt --path reference/decision-rules.md","handlingStrategy":"validation","validationCode":"var rel = (relativePath ?? string.Empty).Replace('\\\\', '/').TrimStart('/');\nif (string.IsNullOrEmpty(rel))\n    throw new ArgumentException(\"path is empty — pass a relative skill file\");\nvar text = SkillInstaller.LoadSkillFile(skillName, rel);","typeGuard":null,"tryCatchPattern":"try { var text = SkillInstaller.LoadSkillFile(skillName, relativePath); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"path is empty\"))\n{ /* prompt for a non-empty relative path */ }","preventionTips":["Always pass a concrete relative path like 'reference/foo.md'.","Guard MCP path= argument against null/empty before calling.","Fetch valid paths from the load_skill manifest or ListSkillFiles."],"tags":["skills","argument","validation","path"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}