{"record":{"id":"d7a9dcf7e3fdaee9","repo":"larksuite/cli","slug":"multiple-plugins-customized-skills-only-one-plugi","errorCode":null,"errorMessage":"multiple plugins customized skills; only one plugin may own skill content","messagePattern":"multiple plugins customized skills; only one plugin may own skill content","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillpolicy/resolver.go","lineNumber":32,"sourceCode":"\t\"io/fs\"\n\n\t\"github.com/larksuite/cli/extension/platform\"\n\t\"github.com/larksuite/cli/internal/skillref\"\n)\n\n// PluginSkill pairs a plugin name with the SkillsOverlay it contributed, so a\n// conflict can be attributed to specific owners. Mirrors\n// cmdpolicy.PluginRule.\ntype PluginSkill struct {\n\tPluginName    string\n\tSkillsOverlay *platform.SkillsOverlay\n}\n\n// ErrMultipleSkillsOverlays reports that more than one plugin tried to\n// customize skill content. Mirrors cmdpolicy.ErrMultipleRestricts: only\n// one owner is allowed so independent plugins cannot silently overwrite\n// each other's skill tree.\nvar ErrMultipleSkillsOverlays = errors.New(\"multiple plugins customized skills; only one plugin may own skill content\")\n\n// ErrNoBaseSkillContent reports that Allow or Remove was requested against an\n// empty base tree. This most often means an external wrapper main omitted\n// cmd.SetEmbeddedSkillContent; exposing a sentinel lets the command layer give\n// the integrator that specific recovery action instead of blaming a skill-name\n// typo.\nvar ErrNoBaseSkillContent = errors.New(\"build embeds no base skill content\")\n\n// ErrInvalidHostBase reports that the wrapper-provided base skill tree is\n// malformed. It is distinct from a plugin's replacement Base so diagnostics\n// can direct the integrator to the correct owner.\nvar ErrInvalidHostBase = errors.New(\"host embedded skill content is invalid\")\n\n// ErrUnsatisfiedSkillDependency reports that a skill retained by the final\n// composed manifest declares another skill that the manifest does not retain.\n// The resolver never widens Allow or overrides Remove to repair this: an\n// incomplete distribution is a build-integrity error.\nvar ErrUnsatisfiedSkillDependency = errors.New(\"composed skill tree has an unsatisfied required skill\")","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillpolicy/resolver.go#L14-L50","documentation":"ErrMultipleSkillsOverlays is a sentinel in internal/skillpolicy returned when more than one plugin declares a SkillsOverlay. Only one plugin may own skill content, mirroring cmdpolicy.ErrMultipleRestricts, so independent plugins cannot silently overwrite each other's skill tree. ResolveWithReferences wraps the sentinel with the list of conflicting owners, and cmd/platform_guards.go maps it to the ReasonMultipleSkillsOverlays failure reason code.","triggerScenarios":"Calling platform/skill resolution (via cmd guards) with two or more plugins whose specs each set a non-nil SkillsOverlay, causing distinctOwners(specs) > 1 in resolver.go:79.","commonSituations":"Installing two plugins that both ship skill customizations; a plugin update that newly added skill content while another plugin already owned it; wrapper configurations combining a bundled plugin and a third-party one that both touch skills.","solutions":["Remove or disable one of the conflicting plugins so exactly one plugin owns skill content.","If both plugins are needed, consolidate their skill customizations into a single plugin/overlay.","Check the error text for the wrapped owner list to identify which plugins conflict before deciding which to keep."],"exampleFix":"// before: two plugins both declare\nPlugins: []Plugin{p1 /* SkillsOverlay: ... */, p2 /* SkillsOverlay: ... */}\n// after: keep a single overlay owner\nPlugins: []Plugin{p1 /* SkillsOverlay: ... */, p2 /* no SkillsOverlay */}","handlingStrategy":"try-catch","validationCode":"owners := 0\nfor _, p := range plugins {\n    if p.SkillsOverlay != nil { owners++ }\n}\nif owners > 1 { return errors.New(\"more than one plugin declares a SkillsOverlay\") }","typeGuard":"if errors.Is(err, skillpolicy.ErrMultipleSkillsOverlays) {\n    // surface owner list and ask user to keep exactly one overlay plugin\n}","tryCatchPattern":"if err := resolve(); err != nil {\n    if errors.Is(err, skillpolicy.ErrMultipleSkillsOverlays) {\n        // map to ReasonMultipleSkillsOverlays and show the wrapped owners\n    }\n}","preventionTips":["Treat skill content ownership as single-owner: audit installed plugins for SkillsOverlay before adding a new one.","Document in plugin guides that skill customization is exclusive.","Surface the wrapped owner list to users so conflicts are diagnosable."],"tags":["go","plugins","skills","configuration-conflict"],"backgroundTag":"duplicate-configuration-owner","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"}