larksuite/cli · error
%s: probing skill %q: %w
Error message
%s: probing skill %q: %w
What it means
fs.Stat on <skill>/SKILL.md returned a real error (not fs.ErrNotExist/fs.ErrInvalid) while probing whether a skill directory is well-formed — e.g. a permission or I/O fault. scanSkillTree distinguishes genuine faults from plain absence, which has its own 'missing SKILL.md' message.
Source
Thrown at internal/skillpolicy/resolver.go:185
snapshot := skillTreeSnapshot{source: source, skills: map[string]skillManifest{}}
if source == nil {
return snapshot, nil
}
entries, err := fs.ReadDir(source, ".")
if err != nil {
return snapshot, fmt.Errorf("%s: cannot read root: %w", label, err)
}
for _, e := range entries {
name := e.Name()
if !e.IsDir() {
return snapshot, fmt.Errorf("%s: %q is not a directory; every %s entry must be a <skill>/ dir", label, name, label)
}
if !isSkillName(name) {
return snapshot, fmt.Errorf("%s: %q is not a valid skill name", label, name)
}
ok, err := skillExists(source, name)
if err != nil {
return snapshot, fmt.Errorf("%s: probing skill %q: %w", label, name, err)
}
if !ok {
return snapshot, fmt.Errorf("%s: skill %q is missing SKILL.md", label, name)
}
manifest, err := readSkillManifest(source, name)
if err != nil {
return snapshot, fmt.Errorf("%s: skill %q has invalid metadata: %w", label, name, err)
}
snapshot.skills[name] = manifest
}
return snapshot, nil
}
// validateSelection rejects allow/remove entries that cannot compose against
// the already-validated base snapshot.
func validateSelection(lower skillTreeSnapshot, spec *platform.SkillsOverlay) error {
if err := validateSkillNames("Allow", spec.Allow); err != nil {
return errView on GitHub (pinned to 7fd6ef3c07)
Solutions
- Inspect the wrapped cause after 'probing skill' for the concrete os/fs error
- Fix filesystem permissions or the mount serving the skill tree
- If a custom fs.FS is used, correct its Stat to return fs.ErrNotExist for absent paths instead of a generic error
- Retry after resolving transient I/O/resource conditions (e.g. close leaked file handles)
Example fix
// before
func (f myFS) Stat(name string) (fs.FileInfo, error) { return nil, errors.New("unsupported") }
// after
func (f myFS) Stat(name string) (fs.FileInfo, error) {
info, err := statImpl(name)
if os.IsNotExist(err) { return nil, fs.ErrNotExist }
return info, err
} Defensive patterns
Strategy: retry
Validate before calling
if _, err := fs.Stat(skillFS, name+"/SKILL.md"); err != nil && !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("stat failed for %s: %w", name, err)
} Try / catch
err := tryResolve()
if errors.Is(err, fs.ErrPermission) || errors.Is(err, syscall.EMFILE) {
// remediate permissions/handles, then retry once
} Prevention
- Ensure the skill tree is readable by the process user
- Return fs.ErrNotExist from custom FS Stat for absent paths
- Monitor file-descriptor limits in long-running hosts
- Avoid network mounts for embedded skill assets
When it happens
Trigger: ResolveWithReferences scanning a Base/Overlay FS where stat'ing name+"/SKILL.md" fails with a non-not-exist error: permission denied, too many open files, network FS outage, or a custom FS whose Stat implementation errors.
Common situations: Read-only or restricted permissions on embedded-unpack dirs, disk-backed FS on a failing mount, resource exhaustion (EMFILE) in long-running plugin hosts, or a buggy custom fs.FS Stat.
Related errors
- %s: cannot read root: %w
- invalid chart size: {size!r}
- %s: %q is not a directory; every %s entry must be a <skill>/
- Invalid column: {column!r}
- Invalid column index: {index}
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/26c472c8d99de4b8.
Report an issue: GitHub.