larksuite/cli · error

%q has an empty relative path

Error message

%q has an empty relative path

What it means

A trailing slash (e.g. "auth/") leaves the path component empty after strings.Cut, which would ambiguously mean "the skill's SKILL.md" while the user clearly asked for a path. Parse rejects this exact shape rather than silently treating it as no path.

Source

Thrown at internal/skillref/ref.go:35

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 {
	if r.Path == "" {
		return r.Skill
	}
	return r.Skill + "/" + r.Path
}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Drop the trailing slash: use "auth" (for SKILL.md) or "auth/references/tokens.md" (for a file).
  2. Trim trailing "/" with strings.TrimRight(raw, "/") before Parse when input is assembled.
  3. If the intent was the skill root, omit the slash entirely — empty Path already means SKILL.md.
  4. Fix the templating/variable so an empty subpath component is skipped instead of leaving a dangling slash.

Example fix

// before
ref, err := skillref.Parse(fmt.Sprintf("%s/%s", skill, subpath)) // "auth/"
// after
raw := skill
if subpath != "" {
    raw = skill + "/" + subpath
}
ref, err := skillref.Parse(raw)
Defensive patterns

Strategy: validation

Validate before calling

raw = strings.TrimRight(raw, "/")
if raw == "" { return errors.New("empty reference") }
ref, err := skillref.Parse(raw)

Try / catch

if err != nil && strings.Contains(err.Error(), "empty relative path") {
    ref, err = skillref.Parse(strings.TrimRight(raw, "/"))
}

Prevention

When it happens

Trigger: Parse("auth/") — trailing slash with no path after it. Common when a base path variable ends with "/" and is concatenated with an empty suffix.

Common situations: Shell scripts building "${SKILL}/${SUBPATH}" where SUBPATH is empty; tab-completion or copy of a directory URL/path including the trailing slash.

Related errors


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