larksuite/cli · error

ErrUnsatisfiedSkillDependency

ErrUnsatisfiedSkillDependency

Error message

%w: skill %q requires skill %q, but %q is absent from the composed skill tree

What it means

validateRequiredSkills walks the composed skill tree and, for each skill's declared requiredSkills, throws a typed ErrUnsatisfiedSkillDependency when a required skill is not present in the composed tree. This is the dependency-closure check: the composition is incomplete, so the skill set cannot be resolved with references safely.

Source

Thrown at internal/skillpolicy/dependencies.go:107

	return out, nil
}

// validateRequiredSkills checks the already-composed owner manifest. It must
// run after Base -> Allow -> Remove -> Overlay so no validation branch can
// accidentally disagree with the tree that list/read actually serves.
func validateRequiredSkills(composed *overlayFS) error {
	if composed == nil {
		return nil
	}
	names := make([]string, 0, len(composed.owner))
	for name := range composed.owner {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		for _, dependency := range composed.owner[name].manifest.requiredSkills {
			if _, present := composed.owner[dependency]; !present {
				return fmt.Errorf("%w: skill %q requires skill %q, but %q is absent from the composed skill tree", ErrUnsatisfiedSkillDependency, name, dependency, dependency)
			}
		}
	}
	return nil
}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Add the missing dependency skill to the composed skill tree / distribution
  2. Fix the dependency name in the requiring skill's SKILL.md to match an existing skill
  3. Include all transitive dependencies in the composition inputs passed to ResolveWithReferences
  4. If the dependency no longer exists, remove or replace the stale entry in requires.skills

Example fix

// before
---
metadata:
  requires:
    skills: [deploy-runbook]   # not shipped
---
// after (ship deploy-runbook/ with SKILL.md, or fix the name)
---
metadata:
  requires:
    skills: [deployment]        # existing skill
---
Defensive patterns

Strategy: validation

Validate before calling

// pre-compute the closure before ResolveWithReferences
manifests := map[string][]string{} // skill -> requiredSkills from SKILL.md
var missing []string
var visit func(string)
visit = func(name string) {
    for _, dep := range manifests[name] {
        if _, ok := manifests[dep]; !ok { missing = append(missing, name+" -> "+dep); continue }
        visit(dep)
    }
}
visit(rootSkill)
if len(missing) > 0 { return fmt.Errorf("unresolved skill dependencies: %v", missing) }

Try / catch

if err := resolver.ResolveWithReferences(...); err != nil {
    if errors.Is(err, ErrUnsatisfiedSkillDependency) {
        return fmt.Errorf("incomplete skill distribution: %w (ship the missing skill or drop the requires entry)", err)
    }
    return err
}

Prevention

When it happens

Trigger: ResolveWithReferences composed a skill tree where skill X's frontmatter lists a dependency Y, but no directory/skill named Y exists in the composed source (Y missing, misspelled, or not included in the composition inputs).

Common situations: A skill declares a dependency on a skill that was never shipped or was renamed; the composition call includes only a subset of skills, omitting a transitive dependency; a typo in the requires list; a plugin distribution trimmed "unused" skills that others depend on.

Related errors


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