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
- Rebuild the binary so embedded builtin skill assets match the current source (go build .)
- Verify the file exists under internal/skills/builtin/ and that the embed pattern includes it
- Check that SkillFilePath minus BuiltinPrefix resolves to the correct path inside the embedded FS
- 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
- Rebuild the binary after pulling changes so embedded assets stay in sync
- Keep builtin skills under internal/skills/builtin/ with embed patterns unchanged
- Treat builtin-read errors as build/deploy issues, not user errors
- Add a startup smoke test that reads every builtin skill ID
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
- skill not found
- name is required
- name must be alphanumeric with hyphens, no leading/trailing/
- description is required
- no YAML frontmatter found
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/7a6fbd45c046e3c2.
Report an issue: GitHub.