larksuite/cli · error
%q has invalid skill name %q
Error message
%q has invalid skill name %q
What it means
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.
Source
Thrown at internal/skillref/ref.go:29
"io/fs"
"strings"
)
// Ref is one exact canonical or runtime skill reference. Path is relative to
// the named skill; an empty Path denotes the skill's SKILL.md.
type Ref struct {
Skill string
Path string
}
// Parse parses the "name[/relative/path]" form accepted by `skills read`.
func Parse(raw string) (Ref, error) {
if raw == "" {
return Ref{}, fmt.Errorf("skill reference is empty")
}
skill, path, _ := strings.Cut(raw, "/")
if !ValidSkillName(skill) {
return Ref{}, fmt.Errorf("%q has invalid skill name %q", raw, skill)
}
if path != "" && (!fs.ValidPath(path) || path == "." || strings.Contains(path, `\`)) {
return Ref{}, fmt.Errorf("%q has invalid relative path %q", raw, path)
}
if path == "" && strings.HasSuffix(raw, "/") {
return Ref{}, fmt.Errorf("%q has an empty relative path", raw)
}
return Ref{Skill: skill, Path: path}, nil
}
// ValidSkillName reports whether name can identify a top-level skill
// directory. Keep this rule aligned with the skill-tree manifest validator.
func ValidSkillName(name string) bool {
return name != "" && name != "." && name != ".." && !strings.ContainsAny(name, `/\`)
}
// String returns the canonical "name[/relative/path]" form.
func (r Ref) String() string {View on GitHub (pinned to 7fd6ef3c07)
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.
Example fix
// before
ref, err := skillref.Parse("\\skills\\auth\\TOKENS.md")
// after
ref, err := skillref.Parse("auth/TOKENS.md") Defensive patterns
Strategy: validation
Validate before calling
skill, _, _ := strings.Cut(raw, "/")
if !skillref.ValidSkillName(skill) {
return fmt.Errorf("reference %q: invalid skill name %q", raw, skill)
} Type guard
func parseableRef(raw string) bool {
skill, _, _ := strings.Cut(raw, "/")
return skillref.ValidSkillName(skill)
} Try / catch
ref, err := skillref.Parse(raw)
if err != nil {
return fmt.Errorf("use <skill> or <skill>/<relative/path>: %w", err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- skill reference is empty
- %q has an empty relative path
- %w: source %q: %w
- name must not be empty
- name %q must not include leading dashes
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/5e5b7ffb9ecb4a00.
Report an issue: GitHub.