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

  1. In the wrapper main, call cmd.SetEmbeddedSkillContent with the embedded base skill FS so Allow/Remove have a base tree.
  2. If the plugin intentionally ships its own tree, populate spec.Base instead of relying on the host base.
  3. If no base tree is wanted, drop the Allow/Remove entries — they are meaningless against an empty base.
  4. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/ca446b18e711d42b. Report an issue: GitHub.