charmbracelet/crush · error

skill not found

Error message

skill not found

What it means

ErrSkillNotFound is the sentinel error returned by Catalog.FindEffective when the requested skill ID is not part of the effective visible skill set (builtin, user, or project skills currently discovered). Callers should compare with errors.Is.

Source

Thrown at internal/skills/catalog.go:41

	Name          string     `json:"name"`
	Description   string     `json:"description"`
	Label         string     `json:"label"`
	Source        SourceType `json:"source"`
	UserInvocable bool       `json:"user_invocable"`
}

// SkillReadResult holds metadata about a skill returned alongside its
// content.
type SkillReadResult struct {
	Name        string     `json:"name"`
	Description string     `json:"description"`
	Source      SourceType `json:"source"`
	Builtin     bool       `json:"builtin"`
}

// ErrSkillNotFound is returned when a skill ID is not part of the
// effective visible skill set.
var ErrSkillNotFound = errors.New("skill not found")

// Catalog builds a slice of CatalogEntry values from pre-discovered
// skills. The skillPaths and workingDir parameters are used only for
// labelling (system / user / project); pass nil/empty when labels are
// not needed.
func Catalog(active []*Skill, skillPaths []string, workingDir string) []CatalogEntry {
	entries := make([]CatalogEntry, 0, len(active))
	for _, skill := range active {
		label, source := skillLabel(skillPaths, workingDir, skill)
		entries = append(entries, CatalogEntry{
			ID:            skill.SkillFilePath,
			Name:          skill.Name,
			Description:   skill.Description,
			Label:         label,
			Source:        source,
			UserInvocable: skill.UserInvocable,
		})
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Run the skill listing (catalog) to see the effective visible skill IDs and use the exact ID
  2. Fix the ID typo or update the stale reference to the renamed skill
  3. Ensure the skill directory exists in .agents/skills, user skills dir, or builtin set and passes visibility filtering

Example fix

// before
skill, err := catalog.FindEffective("code-review")
// after
if !catalog.HasSkill("code-review") {
	return fmt.Errorf("skill %q not available; check skill config", "code-review")
}
skill, err := catalog.FindEffective("code-review")
Defensive patterns

Strategy: type-guard

Validate before calling

for id, ok := range visibleIDs { _ = id; _ = ok }
// before lookup:
if _, ok := catalog.VisibleIDs()[skillID]; !ok {
	return fmt.Errorf("unknown skill %q", skillID)
}

Type guard

func hasSkill(c *skills.Catalog, id string) bool {
	_, err := c.FindEffective(id)
	return err == nil
}

Try / catch

skill, err := catalog.FindEffective(id)
if errors.Is(err, skills.ErrSkillNotFound) {
	// fall back to builtin or report unknown skill
}

Prevention

When it happens

Trigger: Calling FindEffective(id) with a skill ID that was never discovered, was filtered out as non-visible, or whose source directory no longer exists.

Common situations: Typo in a skill ID in config or an agent prompt; a project skill removed/renamed while stale references remain; skills disabled or excluded so they are not in the effective set; running in a directory without the project skill.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/fb5ef68afc5a231b. Report an issue: GitHub.