charmbracelet/crush · error

read builtin skill %q: %w

Error message

read builtin skill %q: %w

What it means

ReadContent, when handling a builtin (embedded) skill, reads the SKILL.md content from the embedded filesystem (BuiltinFS) under the 'builtin/' prefix. This error wraps any failure of that embedded read — effectively meaning the skill is marked Builtin but its file is missing from the embedded FS or the path derived from SkillFilePath is wrong. The underlying fs error is wrapped with %w for errors.Is/As inspection.

Source

Thrown at internal/skills/catalog.go:94

func ReadContent(active []*Skill, skillPaths []string, workingDir string, skillID string) ([]byte, SkillReadResult, error) {
	skill, err := FindEffective(active, skillID)
	if err != nil {
		return nil, SkillReadResult{}, err
	}

	_, source := skillLabel(skillPaths, workingDir, skill)
	result := SkillReadResult{
		Name:        skill.Name,
		Description: skill.Description,
		Source:      source,
		Builtin:     skill.Builtin,
	}

	if skill.Builtin {
		embeddedPath := "builtin/" + strings.TrimPrefix(skill.SkillFilePath, BuiltinPrefix)
		content, err := BuiltinFS().ReadFile(embeddedPath)
		if err != nil {
			return nil, SkillReadResult{}, fmt.Errorf("read builtin skill %q: %w", skillID, err)
		}
		return content, result, nil
	}

	content, err := os.ReadFile(skill.SkillFilePath)
	if err != nil {
		return nil, SkillReadResult{}, fmt.Errorf("read skill %q: %w", skillID, err)
	}
	return content, result, nil
}

func skillLabel(skillPaths []string, workingDir string, skill *Skill) (string, SourceType) {
	if skill.Builtin {
		return string(SourceSystem) + ":" + skill.Name, SourceSystem
	}

	cleanFile := filepath.Clean(skill.SkillFilePath)
	for _, base := range skillPaths {

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Rebuild the binary so embedded builtin skill assets match the current source (go build .)
  2. Verify the file exists under internal/skills/builtin/ and that the embed pattern includes it
  3. Check that SkillFilePath minus BuiltinPrefix resolves to the correct path inside the embedded FS
  4. Update cached skill discovery state so it no longer references the removed builtin skill
Defensive patterns

Strategy: try-catch

Try / catch

content, result, err := skills.ReadContent(active, paths, dir, skillID)
if err != nil {
    var perr error
    if errors.As(err, &perr) && skill.Builtin {
        log.Warn("builtin skill missing from embedded FS", "id", skillID)
        return nil, result // degrade gracefully instead of failing the request
    }
    return err
}

Prevention

When it happens

Trigger: Calling skills.ReadContent with a skill whose SkillFilePath starts with BuiltinPrefix and is flagged Builtin, but BuiltinFS().ReadFile("builtin/<trimmed path>") fails — i.e. the embedded asset was not compiled in or the trimmed path does not match the embedded layout.

Common situations: A builtin skill directory was renamed/removed in a newer build while cached state still references it; embed directives updated so a skill file is no longer included; custom builds excluding the builtin embed.

Related errors


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