larksuite/cli · error
cannot read SKILL.md: %w
Error message
cannot read SKILL.md: %w
What it means
readSkillManifest reads `<skill>/SKILL.md` from the source filesystem to extract the skill's declared dependencies, and wraps any read failure as "cannot read SKILL.md". The library requires every composed skill to ship a SKILL.md manifest; a missing or unreadable one makes the skill's dependency contract unverifiable, so composition fails for that skill.
Source
Thrown at internal/skillpolicy/dependencies.go:25
"fmt"
"io/fs"
"sort"
"strings"
"gopkg.in/yaml.v3"
)
// skillManifest is the build-integrity metadata frozen when a skill tree is
// scanned. Runtime content within the owning skill directory remains live, but
// composition must not be able to change its dependency contract after Resolve.
type skillManifest struct {
requiredSkills []string
}
func readSkillManifest(source fs.FS, name string) (skillManifest, error) {
data, err := fs.ReadFile(source, name+"/SKILL.md")
if err != nil {
return skillManifest{}, fmt.Errorf("cannot read SKILL.md: %w", err)
}
required, err := parseRequiredSkills(name, data)
if err != nil {
return skillManifest{}, err
}
return skillManifest{requiredSkills: required}, nil
}
// parseRequiredSkills reads only the structured hard-dependency declaration:
//
// metadata:
// requires:
// skills: ["lark-shared"]
//
// Markdown links and prose are intentionally irrelevant. A SKILL.md without
// YAML frontmatter declares no hard dependencies; malformed frontmatter that
// purports to be structured metadata fails closed during composition.
func parseRequiredSkills(skillName string, skillMD []byte) ([]string, error) {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Create or restore SKILL.md in the skill directory (with valid frontmatter)
- Fix the skill name/path passed to the resolver so it matches the directory containing SKILL.md
- Verify the fs.FS root: embed the correct directory so `name+"/SKILL.md"` resolves
- Check filename case matches SKILL.md exactly on case-sensitive filesystems
Example fix
// before my-skill/ README.md // no SKILL.md -> "cannot read SKILL.md: ..." // after my-skill/ SKILL.md // with metadata frontmatter README.md
Defensive patterns
Strategy: validation
Validate before calling
// check before composing
if _, err := fs.Stat(source, name+"/SKILL.md"); err != nil {
return fmt.Errorf("skill %q is missing SKILL.md; fix the distribution before resolving", name)
} Try / catch
manifest, err := readSkillManifest(source, name)
if err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrNotExist) {
return fmt.Errorf("skill %q has no SKILL.md (fs root %v): %w", name, pe.Path, err)
}
return err
} Prevention
- Require SKILL.md in every skill directory via CI or packaging checks
- Keep embedded fs.FS roots consistent with skill names
- Match filename case exactly (SKILL.md) for cross-platform builds
- Add a packaging test that reads every shipped skill's SKILL.md
When it happens
Trigger: fs.ReadFile(source, name+"/SKILL.md") returns an error: the skill directory does not contain SKILL.md, the name is misspelled, the fs.FS is rooted differently than expected, or the file exists but cannot be opened.
Common situations: A skill directory added without its SKILL.md; a typo in the skill name passed to the resolver; embedding/embed.FS path mismatch (extra or missing root prefix); a case-sensitivity mismatch (Linux) where the file is named skill.md.
Related errors
- panic: %v
- invalid chart size: {size!r}
- No visible worksheet matched
- +csv-get truncated the scan range at {actual_range}; narrow
- --max-sheets must be at least 1
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/62f0546106d9f4ea.
Report an issue: GitHub.