{"record":{"id":"ca446b18e711d42b","repo":"larksuite/cli","slug":"w-allow-remove-require-skills-in-the-base-tree","errorCode":null,"errorMessage":"%w; Allow/Remove require skills in the base tree","messagePattern":"%w; Allow/Remove require skills in the base tree","errorType":"validation","errorClass":"ErrNoBaseSkillContent","httpStatus":null,"severity":"error","filePath":"internal/skillpolicy/resolver.go","lineNumber":209,"sourceCode":"\t\tif err != nil {\n\t\t\treturn snapshot, fmt.Errorf(\"%s: skill %q has invalid metadata: %w\", label, name, err)\n\t\t}\n\t\tsnapshot.skills[name] = manifest\n\t}\n\treturn snapshot, nil\n}\n\n// validateSelection rejects allow/remove entries that cannot compose against\n// the already-validated base snapshot.\nfunc validateSelection(lower skillTreeSnapshot, spec *platform.SkillsOverlay) error {\n\tif err := validateSkillNames(\"Allow\", spec.Allow); err != nil {\n\t\treturn err\n\t}\n\tif err := validateSkillNames(\"Remove\", spec.Remove); err != nil {\n\t\treturn err\n\t}\n\tif len(lower.skills) == 0 && (len(spec.Allow) > 0 || len(spec.Remove) > 0) {\n\t\treturn fmt.Errorf(\"%w; Allow/Remove require skills in the base tree\", ErrNoBaseSkillContent)\n\t}\n\tif err := validateSkillsInBase(\"Allow\", spec.Allow, lower); err != nil {\n\t\treturn err\n\t}\n\treturn validateSkillsInBase(\"Remove\", spec.Remove, lower)\n}\n\nfunc validateSkillNames(field string, names []string) error {\n\tfor _, name := range names {\n\t\tif !isSkillName(name) {\n\t\t\treturn fmt.Errorf(\"%s: %q is not a valid skill name\", field, name)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc validateSkillsInBase(field string, names []string, base skillTreeSnapshot) error {\n\tfor _, name := range names {","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillpolicy/resolver.go#L191-L227","documentation":"validateSelection rejects Allow or Remove entries when the base skill tree contains no skills at all. The sentinel ErrNoBaseSkillContent signals that the delta is being applied against an empty base — almost always because the wrapper main never embedded base skill content (cmd.SetEmbeddedSkillContent). It is raised by ResolveWithReferences when composing a plugin SkillsOverlay.","triggerScenarios":"ResolveWithReferences with a spec whose Allow or Remove lists are non-empty while lower.skills is empty — i.e. base is nil/empty and spec.Base is nil (or also empty), yet the plugin requests skill selection.","commonSituations":"An external wrapper main omits SetEmbeddedSkillContent so the plugin's Allow/Remove has nothing to act on; a plugin author writes Allow/Remove assuming a base tree that a misconfigured host does not embed.","solutions":["In the wrapper main, call cmd.SetEmbeddedSkillContent with the embedded base skill FS so Allow/Remove have a base tree.","If the plugin intentionally ships its own tree, populate spec.Base instead of relying on the host base.","If no base tree is wanted, drop the Allow/Remove entries — they are meaningless against an empty base.","Use errors.Is(err, skillpolicy.ErrNoBaseSkillContent) at the command layer to emit the integrator-facing hint instead of blaming a skill-name typo."],"exampleFix":"// before (wrapper main)\n// missing: cmd.SetEmbeddedSkillContent(skillFS)\n// after\ncmd.SetEmbeddedSkillContent(baseSkillFS)","handlingStrategy":"validation","validationCode":"if (len(spec.Allow) > 0 || len(spec.Remove) > 0) && base == nil {\n    return errors.New(\"Allow/Remove set but no base skill tree embedded; call cmd.SetEmbeddedSkillContent\")\n}","typeGuard":"func overlayNeedsBase(spec *platform.SkillsOverlay) bool {\n    return spec != nil && (len(spec.Allow) > 0 || len(spec.Remove) > 0)\n}","tryCatchPattern":"resolved, err := skillpolicy.ResolveWithReferences(base, specs)\nif errors.Is(err, skillpolicy.ErrNoBaseSkillContent) {\n    return fmt.Errorf(\"integrator fix: embed base skill content via cmd.SetEmbeddedSkillContent: %w\", err)\n}","preventionTips":["Always call cmd.SetEmbeddedSkillContent in wrapper mains before registering plugins","Only set Allow/Remove when you know a base tree is embedded","Use errors.Is against ErrNoBaseSkillContent in tests to catch missing embedding early"],"tags":["go","skill-policy","empty-base","sentinel-error"],"backgroundTag":"missing-embedded-skill-base","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"}