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 err

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Inspect the wrapped cause after 'probing skill' for the concrete os/fs error
  2. Fix filesystem permissions or the mount serving the skill tree
  3. If a custom fs.FS is used, correct its Stat to return fs.ErrNotExist for absent paths instead of a generic error
  4. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/26c472c8d99de4b8. Report an issue: GitHub.