cli/cli · error

%s is not a directory

Error message

%s is not a directory

What it means

DiscoverLocalSkillsWithOptions stats the path successfully but it is not a directory (e.g. a regular file, socket, or symlink to a file). Discovery needs to walk a directory tree, so it refuses immediately with this explicit message instead of a confusing walk error.

Source

Thrown at internal/skills/discovery/discovery.go:985

		)
	}
	return skills, nil
}

// DiscoverLocalSkillsWithOptions finds skills in a local directory using the
// same conventions as remote discovery, with configurable discovery behavior.
func DiscoverLocalSkillsWithOptions(dir string, opts DiscoverOptions) ([]Skill, error) {
	absDir, err := filepath.Abs(dir)
	if err != nil {
		return nil, fmt.Errorf("could not resolve path: %w", err)
	}

	info, err := os.Stat(absDir)
	if err != nil {
		return nil, fmt.Errorf("could not access %s: %w", dir, err)
	}
	if !info.IsDir() {
		return nil, fmt.Errorf("%s is not a directory", dir)
	}

	if _, err := os.Stat(filepath.Join(absDir, "SKILL.md")); err == nil {
		skill, err := localSkillFromDir(absDir)
		if err != nil {
			return nil, err
		}
		skill.Path = "."
		return []Skill{*skill}, nil
	}

	var skills []Skill
	seen := make(map[string]bool)

	err = filepath.Walk(absDir, func(p string, info os.FileInfo, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}

View on GitHub (pinned to 0eeec0b92e)

Solutions

  1. Pass the directory that contains the skills, e.g. the repo root or the skills/ folder, not the SKILL.md file
  2. If pointing at a single-skill directory, give the directory that holds SKILL.md (its parent), per the documented layouts
  3. For symlinked paths, confirm the link target is a directory: readlink -f <path>

Example fix

# before
discovery.DiscoverLocalSkills("repo/skills/my-skill/SKILL.md")

# after
discovery.DiscoverLocalSkills("repo")
Defensive patterns

Strategy: validation

Validate before calling

if info, err := os.Stat(dir); err == nil && !info.IsDir() {
    return fmt.Errorf("%s is a file; pass its directory (or the skills root) instead", dir)
}

Prevention

When it happens

Trigger: Passing a path to a SKILL.md file or any regular file instead of its containing directory; passing a symlink that resolves to a file.

Common situations: Users assuming the API takes the manifest file path rather than a directory; shell variables that expand to a file (e.g. $SKILL_PATH set to .../skills/my-skill/SKILL.md); tab-completion landing on the file.

Related errors


AI-assisted analysis of cli/cli@0eeec0b92e (2026-08-15). Data as JSON: /api/errors/59f53a40bd909f82. Report an issue: GitHub.