larksuite/cli · error · ErrNoBaseSkillContent
%w; Allow/Remove require skills in the base tree
Error message
%w; Allow/Remove require skills in the base tree
What it means
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.
Source
Thrown at internal/skillpolicy/resolver.go:209
if err != nil {
return snapshot, fmt.Errorf("%s: skill %q has invalid metadata: %w", label, name, err)
}
snapshot.skills[name] = manifest
}
return snapshot, nil
}
// validateSelection rejects allow/remove entries that cannot compose against
// the already-validated base snapshot.
func validateSelection(lower skillTreeSnapshot, spec *platform.SkillsOverlay) error {
if err := validateSkillNames("Allow", spec.Allow); err != nil {
return err
}
if err := validateSkillNames("Remove", spec.Remove); err != nil {
return err
}
if len(lower.skills) == 0 && (len(spec.Allow) > 0 || len(spec.Remove) > 0) {
return fmt.Errorf("%w; Allow/Remove require skills in the base tree", ErrNoBaseSkillContent)
}
if err := validateSkillsInBase("Allow", spec.Allow, lower); err != nil {
return err
}
return validateSkillsInBase("Remove", spec.Remove, lower)
}
func validateSkillNames(field string, names []string) error {
for _, name := range names {
if !isSkillName(name) {
return fmt.Errorf("%s: %q is not a valid skill name", field, name)
}
}
return nil
}
func validateSkillsInBase(field string, names []string, base skillTreeSnapshot) error {
for _, name := range names {View on GitHub (pinned to 7fd6ef3c07)
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.
Example fix
// before (wrapper main) // missing: cmd.SetEmbeddedSkillContent(skillFS) // after cmd.SetEmbeddedSkillContent(baseSkillFS)
Defensive patterns
Strategy: validation
Validate before calling
if (len(spec.Allow) > 0 || len(spec.Remove) > 0) && base == nil {
return errors.New("Allow/Remove set but no base skill tree embedded; call cmd.SetEmbeddedSkillContent")
} Type guard
func overlayNeedsBase(spec *platform.SkillsOverlay) bool {
return spec != nil && (len(spec.Allow) > 0 || len(spec.Remove) > 0)
} Try / catch
resolved, err := skillpolicy.ResolveWithReferences(base, specs)
if errors.Is(err, skillpolicy.ErrNoBaseSkillContent) {
return fmt.Errorf("integrator fix: embed base skill content via cmd.SetEmbeddedSkillContent: %w", err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- %s: skill %q is missing SKILL.md
- %s: skill %q has invalid metadata: %w
- %s: skill %q is not in the base tree
- %w: source %q: %w
- Invalid column: {column!r}
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/ca446b18e711d42b.
Report an issue: GitHub.