{"record":{"id":"5e5b7ffb9ecb4a00","repo":"larksuite/cli","slug":"q-has-invalid-skill-name-q","errorCode":null,"errorMessage":"%q has invalid skill name %q","messagePattern":"%q has invalid skill name %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillref/ref.go","lineNumber":29,"sourceCode":"\t\"io/fs\"\n\t\"strings\"\n)\n\n// Ref is one exact canonical or runtime skill reference. Path is relative to\n// the named skill; an empty Path denotes the skill's SKILL.md.\ntype Ref struct {\n\tSkill string\n\tPath  string\n}\n\n// Parse parses the \"name[/relative/path]\" form accepted by `skills read`.\nfunc Parse(raw string) (Ref, error) {\n\tif raw == \"\" {\n\t\treturn Ref{}, fmt.Errorf(\"skill reference is empty\")\n\t}\n\tskill, path, _ := strings.Cut(raw, \"/\")\n\tif !ValidSkillName(skill) {\n\t\treturn Ref{}, fmt.Errorf(\"%q has invalid skill name %q\", raw, skill)\n\t}\n\tif path != \"\" && (!fs.ValidPath(path) || path == \".\" || strings.Contains(path, `\\`)) {\n\t\treturn Ref{}, fmt.Errorf(\"%q has invalid relative path %q\", raw, path)\n\t}\n\tif path == \"\" && strings.HasSuffix(raw, \"/\") {\n\t\treturn Ref{}, fmt.Errorf(\"%q has an empty relative path\", raw)\n\t}\n\treturn Ref{Skill: skill, Path: path}, nil\n}\n\n// ValidSkillName reports whether name can identify a top-level skill\n// directory. Keep this rule aligned with the skill-tree manifest validator.\nfunc ValidSkillName(name string) bool {\n\treturn name != \"\" && name != \".\" && name != \"..\" && !strings.ContainsAny(name, `/\\`)\n}\n\n// String returns the canonical \"name[/relative/path]\" form.\nfunc (r Ref) String() string {","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillref/ref.go#L11-L47","documentation":"Parse splits the reference on the first \"/\" and checks the skill component with ValidSkillName (non-empty, not \".\"/\"..\", no \"/\" or \"\\\\\"). The error includes both the original reference and the offending extracted name. It guards the path component from being smuggled into the skill name.","triggerScenarios":"Parse(\"Auth\") is fine, but Parse(\"\"), Parse(\".\"), Parse(\"..\"), Parse(\"a/b/c\" where the first segment is invalid), Parse(\"my\\\\skill\"), or Parse with a leading/trailing slash-producing empty name.","commonSituations":"Windows backslash separators leaking from path handling; absolute paths like \"/auth/TOKENS.md\" yielding an empty leading name; dots used to express current-directory-relative refs; concatenating a path prefix with the skill name.","solutions":["Pass the bare skill name (plus optional \"relative/path\" after one slash), e.g. \"auth/TOKENS.md\", not \"/auth\" or \"my\\\\skill\".","Normalize any input path first with filepath.ToSlash and strip leading \"/\" before Parse.","Pre-validate with skillref.ValidSkillName on the first strings.Cut segment.","Use a valid skill id from `skills list` output instead of a filesystem path."],"exampleFix":"// before\nref, err := skillref.Parse(\"\\\\skills\\\\auth\\\\TOKENS.md\")\n// after\nref, err := skillref.Parse(\"auth/TOKENS.md\")","handlingStrategy":"validation","validationCode":"skill, _, _ := strings.Cut(raw, \"/\")\nif !skillref.ValidSkillName(skill) {\n    return fmt.Errorf(\"reference %q: invalid skill name %q\", raw, skill)\n}","typeGuard":"func parseableRef(raw string) bool {\n    skill, _, _ := strings.Cut(raw, \"/\")\n    return skillref.ValidSkillName(skill)\n}","tryCatchPattern":"ref, err := skillref.Parse(raw)\nif err != nil {\n    return fmt.Errorf(\"use <skill> or <skill>/<relative/path>: %w\", err)\n}","preventionTips":["Normalize host paths with filepath.ToSlash and strip leading separators before Parse","Take the skill id from `skills list`, not from a filesystem path","Document the \"name[/relative/path]\" grammar wherever references are accepted"],"tags":["go","skillref","input-validation","naming"],"backgroundTag":"invalid-skill-name","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}