{"record":{"id":"2b8ada14057355f2","repo":"larksuite/cli","slug":"errmultipleskillsoverlays","errorCode":"ErrMultipleSkillsOverlays","errorMessage":"%w: %v","messagePattern":"%w: %v","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillpolicy/resolver.go","lineNumber":79,"sourceCode":"func resolveContent(base fs.FS, specs []PluginSkill) (fs.FS, error) {\n\tresolved, err := ResolveWithReferences(base, specs)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn resolved.Content, nil\n}\n\n// ResolveWithReferences composes the effective skill tree and its canonical\n// reference projection. base is the CLI's embedded skill FS (nil when the build\n// embeds none). With no spec, content is unchanged and references resolve by\n// identity when the target exists. With exactly one spec it applies, in fixed\n// order, Base override -> Allow -> Remove -> Overlay, then validates and\n// snapshots ReferenceRemaps against the composed tree. Two or more distinct\n// owners is a configuration error.\nfunc ResolveWithReferences(base fs.FS, specs []PluginSkill) (Resolution, error) {\n\towners := distinctOwners(specs)\n\tif len(owners) > 1 {\n\t\treturn Resolution{}, fmt.Errorf(\"%w: %v\", ErrMultipleSkillsOverlays, owners)\n\t}\n\tif len(specs) == 0 || specs[0].SkillsOverlay == nil {\n\t\trefs, err := skillref.New(base, nil)\n\t\tif err != nil {\n\t\t\treturn Resolution{}, err\n\t\t}\n\t\treturn Resolution{Content: base, References: refs}, nil\n\t}\n\n\towner, spec := specs[0].PluginName, specs[0].SkillsOverlay\n\tlower := base\n\tlowerLabel := \"host Base\"\n\tif spec.Base != nil {\n\t\tlower = protectPluginFS(owner, \"Base\", spec.Base)\n\t\tlowerLabel = \"plugin Base\"\n\t}\n\tupper := protectPluginFS(owner, \"Overlay\", spec.Overlay)\n\tlowerSnapshot, err := scanSkillTree(lowerLabel, lower)","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillpolicy/resolver.go#L61-L97","documentation":"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.","triggerScenarios":"Calling ResolveWithReferences (via buildInternalWithConfig or resolveContent) with a []PluginSkill whose entries have two or more distinct PluginName values.","commonSituations":"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.","solutions":["Remove all but one SkillsOverlay-contributing plugin from the plugin set passed to ResolveWithReferences.","If multiple plugins are required, merge their skill deltas into a single overlay owned by one plugin name.","Audit plugin loading code for duplicate registrations under different names.","Use errors.Is(err, skillpolicy.ErrMultipleSkillsOverlays) and parse the appended owner list to see which plugins conflict."],"exampleFix":"// before\nspecs := []skillpolicy.PluginSkill{\n    {PluginName: \"alpha\", SkillsOverlay: overlayA},\n    {PluginName: \"beta\", SkillsOverlay: overlayB}, // second owner: error\n}\n// after\nspecs := []skillpolicy.PluginSkill{\n    {PluginName: \"alpha\", SkillsOverlay: mergedOverlay}, // single owner\n}","handlingStrategy":"validation","validationCode":"owners := map[string]bool{}\nfor _, p := range loadedPlugins {\n    if p.SkillsOverlay != nil {\n        owners[p.Name] = true\n    }\n}\nif len(owners) > 1 {\n    return fmt.Errorf(\"refusing to start: %d plugins define SkillsOverlay: %v\", len(owners), owners)\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n    if errors.Is(err, skillpolicy.ErrMultipleSkillsOverlays) {\n        return fmt.Errorf(\"plugin conflict — keep only one overlay plugin: %w\", err)\n    }\n    return err\n}","preventionTips":["Before loading plugins, count how many expose a SkillsOverlay and reject multiples early.","Document that skill customization is single-owner in your plugin distribution.","Detect duplicate registrations under different names in plugin loading code.","Use errors.Is(err, ErrMultipleSkillsOverlays) for precise handling."],"tags":["go","configuration","plugin-conflict","skills-overlay"],"backgroundTag":"multiple-plugin-overlay-conflict","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"}