{"record":{"id":"b8fd9a2246b34f32","repo":"larksuite/cli","slug":"plugin-q-skill-spec-w","errorCode":null,"errorMessage":"plugin %q skill spec: %w","messagePattern":"plugin %q skill spec: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillpolicy/resolver.go","lineNumber":102,"sourceCode":"\t\t\treturn Resolution{}, err\n\t\t}\n\t\treturn Resolution{Content: base, References: refs}, nil\n\t}\n\n\towner, spec := specs[0].PluginName, specs[0].SkillsOverlay\n\tlower := base\n\tlowerLabel := \"host Base\"\n\tif spec.Base != nil {\n\t\tlower = protectPluginFS(owner, \"Base\", spec.Base)\n\t\tlowerLabel = \"plugin Base\"\n\t}\n\tupper := protectPluginFS(owner, \"Overlay\", spec.Overlay)\n\tlowerSnapshot, err := scanSkillTree(lowerLabel, lower)\n\tif err != nil {\n\t\tif spec.Base == nil {\n\t\t\treturn Resolution{}, fmt.Errorf(\"%w: %w\", ErrInvalidHostBase, err)\n\t\t}\n\t\treturn Resolution{}, fmt.Errorf(\"plugin %q skill spec: %w\", owner, err)\n\t}\n\tupperSnapshot, err := scanSkillTree(\"plugin Overlay\", upper)\n\tif err != nil {\n\t\treturn Resolution{}, fmt.Errorf(\"plugin %q skill spec: %w\", owner, err)\n\t}\n\tif err := validateSelection(lowerSnapshot, spec); err != nil {\n\t\treturn Resolution{}, fmt.Errorf(\"plugin %q skill spec: %w\", owner, err)\n\t}\n\tvar content fs.FS\n\tif lower == nil && upper == nil {\n\t\tcontent = nil\n\t} else {\n\t\tcomposed := newOverlayFS(lowerSnapshot, upperSnapshot, spec.Remove, spec.Allow)\n\t\tif err := validateRequiredSkills(composed); err != nil {\n\t\t\treturn Resolution{}, fmt.Errorf(\"plugin %q skill spec: %w\", owner, err)\n\t\t}\n\t\tcontent = composed\n\t}","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillpolicy/resolver.go#L84-L120","documentation":"A generic attribution wrapper: a plugin's skill spec (Base, Overlay, Allow/Remove selection, required-skill closure, or ReferenceRemaps) failed validation or scanning, and the resolver wraps the cause as 'plugin \"<owner>\" skill spec: <cause>'. It applies whenever the failure is attributable to the plugin's contribution rather than the host base (which uses ErrInvalidHostBase).","triggerScenarios":"ResolveWithReferences with a spec whose Base is non-nil and scanSkillTree fails on plugin Base; scanSkillTree fails on plugin Overlay; validateSelection rejects Allow/Remove names (invalid name, not in base, or no base content); validateRequiredSkills finds an unsatisfied dependency; resolveReferences hits a bad remap.","commonSituations":"A plugin ships an Overlay directory containing a stray file at top level; Allow references a skill name absent from the base; Remove names a skill the base never had; a retained skill requires another skill the plugin removed; a ReferenceRemap points at a nonexistent target.","solutions":["Unwrap with errors.Is/errors.As to find the specific cause (ErrNoBaseSkillContent, ErrUnsatisfiedSkillDependency, skillref.ErrInvalidRemap, or a scan error naming the bad skill).","Fix the plugin's SkillsOverlay: keep top-level entries as valid <skill>/ dirs with valid SKILL.md.","Align Allow/Remove with the actual base tree skill names, and never remove a skill that a retained skill requires.","Validate ReferenceRemaps against the composed tree — both From and To must be parseable and the target must exist."],"exampleFix":"// before (overlay has a stray top-level file)\noverlay/README.md\noverlay/my-skill/SKILL.md\n// after — every top-level entry must be a skill directory\noverlay/my-skill/SKILL.md\noverlay/other-skill/SKILL.md","handlingStrategy":"validation","validationCode":"// pre-validate a plugin SkillsOverlay before registration\nfor _, e := range must(fs.ReadDir(overlay, \".\")) { // every top entry must be a skill dir\n    if !e.IsDir() { return fmt.Errorf(\"overlay: %q is not a skill dir\", e.Name()) }\n    if _, err := fs.Stat(overlay, e.Name()+\"/SKILL.md\"); err != nil {\n        return fmt.Errorf(\"overlay skill %q missing SKILL.md\", e.Name())\n    }\n}\nfor _, name := range spec.Allow { /* must exist in base */ }\nfor _, name := range spec.Remove { /* must exist in base and not be required */ }","typeGuard":null,"tryCatchPattern":"if err != nil {\n    var ownedErr error\n    if errors.As(err, new(*fs.PathError)) || strings.Contains(err.Error(), \"skill spec:\") {\n        return fmt.Errorf(\"plugin skill spec invalid — check Overlay/Allow/Remove/ReferenceRemaps: %w\", err)\n    }\n    return err\n}","preventionTips":["Keep the Overlay top level exclusively valid <skill>/ directories with SKILL.md.","Cross-check Allow/Remove names against the actual base tree before shipping.","Never remove a skill required by another retained skill.","Validate ReferenceRemaps targets exist in the composed tree; test resolution in the plugin's CI."],"tags":["go","plugin-configuration","skills-overlay","validation"],"backgroundTag":"invalid-plugin-skill-spec","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"}