{"record":{"id":"b2757bed3483bfc8","repo":"larksuite/cli","slug":"composed-skill-tree-has-an-unsatisfied-required-sk","errorCode":null,"errorMessage":"composed skill tree has an unsatisfied required skill","messagePattern":"composed skill tree has an unsatisfied required skill","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillpolicy/resolver.go","lineNumber":50,"sourceCode":"var ErrMultipleSkillsOverlays = errors.New(\"multiple plugins customized skills; only one plugin may own skill content\")\n\n// ErrNoBaseSkillContent reports that Allow or Remove was requested against an\n// empty base tree. This most often means an external wrapper main omitted\n// cmd.SetEmbeddedSkillContent; exposing a sentinel lets the command layer give\n// the integrator that specific recovery action instead of blaming a skill-name\n// typo.\nvar ErrNoBaseSkillContent = errors.New(\"build embeds no base skill content\")\n\n// ErrInvalidHostBase reports that the wrapper-provided base skill tree is\n// malformed. It is distinct from a plugin's replacement Base so diagnostics\n// can direct the integrator to the correct owner.\nvar ErrInvalidHostBase = errors.New(\"host embedded skill content is invalid\")\n\n// ErrUnsatisfiedSkillDependency reports that a skill retained by the final\n// composed manifest declares another skill that the manifest does not retain.\n// The resolver never widens Allow or overrides Remove to repair this: an\n// incomplete distribution is a build-integrity error.\nvar ErrUnsatisfiedSkillDependency = errors.New(\"composed skill tree has an unsatisfied required skill\")\n\n// Resolution is the build-local result of composing embedded skill assets.\n// Content serves `skills list`/`read`; References projects canonical\n// CLI-authored pointers onto that same tree.\ntype Resolution struct {\n\tContent    fs.FS\n\tReferences *skillref.Resolver\n}\n\n// resolveContent is a test convenience over the production resolution path.\nfunc resolveContent(base fs.FS, specs []PluginSkill) (fs.FS, error) {\n\tresolved, err := ResolveWithReferences(base, specs)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn resolved.Content, nil\n}\n","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillpolicy/resolver.go#L32-L68","documentation":"ErrUnsatisfiedSkillDependency reports that a skill retained in the final composed manifest declares a required skill that the composed tree does not retain. The resolver intentionally never widens Allow or overrides Remove to repair this: an incomplete distribution is treated as a build-integrity error and fails closed. The check runs in internal/skillpolicy/dependencies.go over composed.owner's requiredSkills.","triggerScenarios":"Removing a skill that another retained skill requires (Remove-based), allowing a subset that omits a dependency (Allow-based), or a plugin overlay replacement whose own Base omits a skill its retained skills require — each produce the wrapped sentinel with the missing dependency name.","commonSituations":"Plugin authors pruning 'unused' skills that are actually runtime dependencies; tightening an Allow list after a dependency was added upstream; UTF-8 BOM or naming changes making a dependency name mismatch (see TestResolve_UTF8BOMFrontmatterMissingRequiredSkillFailsClosed).","solutions":["Read the wrapped error text to find which skill requires which missing dependency, then keep the dependency in the composed tree.","Adjust the plugin's Allow list to include the required skill, or drop the Remove entry that deletes it.","If a skill's requirement is wrong, fix the requiredSkills declaration in that skill's frontmatter so it no longer demands a skill you don't ship."],"exampleFix":"// before\nspec.Allow = []string{\"skill-a\"} // skill-a requires skill-b\n// after\nspec.Allow = []string{\"skill-a\", \"skill-b\"}","handlingStrategy":"validation","validationCode":"func checkDeps(retained, all map[string]Skill) error {\n    for name, s := range retained {\n        for _, dep := range s.RequiredSkills {\n            if _, ok := retained[dep]; !ok {\n                return fmt.Errorf(\"%s requires %s which is not retained\", name, dep)\n            }\n        }\n    }\n    return nil\n}","typeGuard":"if errors.Is(err, skillpolicy.ErrUnsatisfiedSkillDependency) {\n    // read wrapped message for the missing dependency name\n}","tryCatchPattern":"if err := resolve(); err != nil {\n    if errors.Is(err, skillpolicy.ErrUnsatisfiedSkillDependency) {\n        // widen Allow or drop the offending Remove to include the dependency\n    }\n}","preventionTips":["Before pruning an Allow list, check each candidate skill's requiredSkills in its frontmatter.","Never Remove a skill without grepping the tree for skills that require it.","Add resolver tests asserting dependency completeness for your plugin's overlay."],"tags":["go","skills","dependencies","build-integrity","plugin"],"backgroundTag":"missing-dependency","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}