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

  1. Pass the bare skill name (plus optional "relative/path" after one slash), e.g. "auth/TOKENS.md", not "/auth" or "my\\skill".
  2. Normalize any input path first with filepath.ToSlash and strip leading "/" before Parse.
  3. Pre-validate with skillref.ValidSkillName on the first strings.Cut segment.
  4. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/5e5b7ffb9ecb4a00. Report an issue: GitHub.