{"record":{"id":"12eab54bc527420b","repo":"larksuite/cli","slug":"s-skill-q-is-not-in-the-base-tree","errorCode":null,"errorMessage":"%s: skill %q is not in the base tree","messagePattern":"(.+?): skill %q is not in the base tree","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillpolicy/resolver.go","lineNumber":229,"sourceCode":"\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 {\n\t\tif _, exists := base.skills[name]; !exists {\n\t\t\treturn fmt.Errorf(\"%s: skill %q is not in the base tree\", field, name)\n\t\t}\n\t}\n\treturn nil\n}\n\n// skillExists reports whether fsys holds a skill named name -- a\n// directory carrying SKILL.md, the shape internal/skillcontent treats as\n// a skill.\nfunc skillExists(fsys fs.FS, name string) (bool, error) {\n\tif fsys == nil {\n\t\treturn false, nil\n\t}\n\tinfo, err := fs.Stat(fsys, name+\"/SKILL.md\")\n\tswitch {\n\tcase err == nil:\n\t\treturn !info.IsDir(), nil\n\tcase errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrInvalid):\n\t\treturn false, nil","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillpolicy/resolver.go#L211-L247","documentation":"validateSkillsInBase verifies that every Allow/Remove skill name exists in the snapshotted base tree. Names must pass syntax checks first (validateSkillNames); this check is semantic — the skill simply is not present in the host base or plugin Base. The resolver never silently ignores missing names.","triggerScenarios":"ResolveWithReferences with spec.Allow or spec.Remove naming a skill absent from lower.skills: a typo, a skill that only exists in the plugin's Overlay, or a skill removed/renamed in the CLI version being built against.","commonSituations":"Allow-listing a skill the plugin itself adds via Overlay (it must be in base, not the overlay, to be selectable); referencing a skill renamed upstream after a CLI upgrade; misspelling the skill directory name.","solutions":["Run `lark-cli skills list` against the same build to see the actual base skill names, then correct the entry.","If the skill is contributed by this plugin's Overlay, it does not need Allow — remove the entry.","If the skill was renamed upstream, update the name in the plugin spec to the new identifier.","Pre-validate by scanning the base FS (or snapshot) for the name before constructing the SkillsOverlay in tests."],"exampleFix":"// before\nspec.Remove = []string{\"skills-managment\"}\n// after\nspec.Remove = []string{\"skills-management\"}","handlingStrategy":"validation","validationCode":"names, err := collectBaseSkillNames(baseFS)\nif err != nil { return err }\nfor _, n := range spec.Allow {\n    if !slices.Contains(names, n) { return fmt.Errorf(\"%q not in base tree\", n) }\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n    var candidates []string\n    return fmt.Errorf(\"unknown skill (run `lark-cli skills list`); did you mean one of %v?: %w\", candidates, err)\n}","preventionTips":["Copy skill names from the current build's `skills list`, not from memory or old docs","Remember Allow/Remove reference base-tree skills, not skills your own Overlay adds","Re-run dry-run tests after CLI upgrades that may rename skills"],"tags":["go","skill-policy","reference-resolution","config-mismatch"],"backgroundTag":"skill-not-in-base-tree","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"}