larksuite/cli · error

ErrMultipleSkillsOverlays

ErrMultipleSkillsOverlays

Error message

%w: %v

What it means

ErrMultipleSkillsOverlays signals that more than one distinct plugin contributed a SkillsOverlay. The resolver allows at most one owner of skill content so independent plugins cannot silently overwrite each other's skill tree; two or more distinct owners is a configuration error listing the offending plugin names.

Source

Thrown at internal/skillpolicy/resolver.go:79

func resolveContent(base fs.FS, specs []PluginSkill) (fs.FS, error) {
	resolved, err := ResolveWithReferences(base, specs)
	if err != nil {
		return nil, err
	}
	return resolved.Content, nil
}

// ResolveWithReferences composes the effective skill tree and its canonical
// reference projection. base is the CLI's embedded skill FS (nil when the build
// embeds none). With no spec, content is unchanged and references resolve by
// identity when the target exists. With exactly one spec it applies, in fixed
// order, Base override -> Allow -> Remove -> Overlay, then validates and
// snapshots ReferenceRemaps against the composed tree. Two or more distinct
// owners is a configuration error.
func ResolveWithReferences(base fs.FS, specs []PluginSkill) (Resolution, error) {
	owners := distinctOwners(specs)
	if len(owners) > 1 {
		return Resolution{}, fmt.Errorf("%w: %v", ErrMultipleSkillsOverlays, owners)
	}
	if len(specs) == 0 || specs[0].SkillsOverlay == nil {
		refs, err := skillref.New(base, nil)
		if err != nil {
			return Resolution{}, err
		}
		return Resolution{Content: base, References: refs}, nil
	}

	owner, spec := specs[0].PluginName, specs[0].SkillsOverlay
	lower := base
	lowerLabel := "host Base"
	if spec.Base != nil {
		lower = protectPluginFS(owner, "Base", spec.Base)
		lowerLabel = "plugin Base"
	}
	upper := protectPluginFS(owner, "Overlay", spec.Overlay)
	lowerSnapshot, err := scanSkillTree(lowerLabel, lower)

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Remove all but one SkillsOverlay-contributing plugin from the plugin set passed to ResolveWithReferences.
  2. If multiple plugins are required, merge their skill deltas into a single overlay owned by one plugin name.
  3. Audit plugin loading code for duplicate registrations under different names.
  4. Use errors.Is(err, skillpolicy.ErrMultipleSkillsOverlays) and parse the appended owner list to see which plugins conflict.

Example fix

// before
specs := []skillpolicy.PluginSkill{
    {PluginName: "alpha", SkillsOverlay: overlayA},
    {PluginName: "beta", SkillsOverlay: overlayB}, // second owner: error
}
// after
specs := []skillpolicy.PluginSkill{
    {PluginName: "alpha", SkillsOverlay: mergedOverlay}, // single owner
}
Defensive patterns

Strategy: validation

Validate before calling

owners := map[string]bool{}
for _, p := range loadedPlugins {
    if p.SkillsOverlay != nil {
        owners[p.Name] = true
    }
}
if len(owners) > 1 {
    return fmt.Errorf("refusing to start: %d plugins define SkillsOverlay: %v", len(owners), owners)
}

Try / catch

if err != nil {
    if errors.Is(err, skillpolicy.ErrMultipleSkillsOverlays) {
        return fmt.Errorf("plugin conflict — keep only one overlay plugin: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling ResolveWithReferences (via buildInternalWithConfig or resolveContent) with a []PluginSkill whose entries have two or more distinct PluginName values.

Common situations: A host integration loads several plugins that each ship a SkillsOverlay; an accidentally duplicated plugin registration under two names; a plugin distribution update added a second overlay-contributing plugin.

Related errors


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