{"record":{"id":"af73dda5faecb026","repo":"larksuite/cli","slug":"multiple-plugins-called-restrict-only-one-plugin","errorCode":null,"errorMessage":"multiple plugins called Restrict; only one plugin may own the policy","messagePattern":"multiple plugins called Restrict; only one plugin may own the policy","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmdpolicy/resolver.go","lineNumber":40,"sourceCode":")\n\ntype ResolveSource struct {\n\tKind SourceKind\n\tName string\n}\n\ntype PluginRule struct {\n\tPluginName string\n\tRule       *platform.Rule\n}\n\ntype Sources struct {\n\tPluginRules []PluginRule\n\tYAMLRules   []*platform.Rule\n\tYAMLPath    string\n}\n\nvar ErrMultipleRestricts = errors.New(\"multiple plugins called Restrict; only one plugin may own the policy\")\n\n// Resolve picks by precedence: plugin > yaml > none, returning the full\n// rule set the winning source contributes. Pure function; load yaml via\n// LoadYAMLPolicy first. Every returned rule is validated.\n//\n// Multi-rule semantics (single owner): one plugin may contribute several\n// rules (each a scoped grant, OR-combined by the engine), but two or more\n// DISTINCT plugins contributing rules is still a configuration error --\n// the resolver aborts so independent plugins cannot silently widen each\n// other's policy. yaml may likewise carry several rules under \"rules:\".\nfunc Resolve(s Sources) ([]*platform.Rule, ResolveSource, error) {\n\towners := distinctOwners(s.PluginRules)\n\tif len(owners) > 1 {\n\t\treturn nil, ResolveSource{}, fmt.Errorf(\"%w: %v\", ErrMultipleRestricts, owners)\n\t}\n\n\tif len(s.PluginRules) > 0 {\n\t\trules := make([]*platform.Rule, 0, len(s.PluginRules))","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/cmdpolicy/resolver.go#L22-L58","documentation":"ErrMultipleRestricts indicates the platform policy resolver found more than one plugin calling Restrict() during initialization. The policy can only have a single owning plugin; when distinctOwners(PluginRules) yields more than one owner, Resolve fails with this sentinel plus the conflicting owner names. It exists so the failure is explicit and classifiable (ReasonMultipleRestricts) rather than an ambiguous precedence decision.","triggerScenarios":"Loading a plugin set where two or more plugins each call cmdpolicy Restrict(); calling Resolve (or the platform guard wiring in cmd/platform_guards.go) with such a rule list.","commonSituations":"Installing two policy-restricting plugins (e.g. a readonly plugin plus a custom company policy plugin) that both try to own the guard policy; misconfigured plugin manifest enabling a second restrictor unintentionally.","solutions":["Remove or disable one of the plugins calling Restrict so exactly one owns the policy","Consolidate both rule sets into a single restricting plugin","Check the plugin list/config for accidentally duplicated policy plugins"],"exampleFix":"// before\nplugins := []Plugin{readonlyPlugin, companyPolicyPlugin} // both call Restrict\n// after\nplugins := []Plugin{companyPolicyPlugin} // single policy owner","handlingStrategy":"validation","validationCode":"owners := distinctOwners(pluginRules)\nif len(owners) > 1 {\n    return fmt.Errorf(\"multiple policy plugins: %v\", owners)\n}","typeGuard":"func isMultipleRestricts(err error) bool { return errors.Is(err, cmdpolicy.ErrMultipleRestricts) }","tryCatchPattern":"policy, err := cmdpolicy.Resolve(sources)\nif errors.Is(err, cmdpolicy.ErrMultipleRestricts) {\n    // inspect err text for owner list, disable one plugin\n}","preventionTips":["Ship at most one plugin that calls Restrict","Document policy ownership in your plugin set's manifest","Audit installed plugins after upgrades for new Restrict callers","Add a startup check listing which plugin owns policy"],"tags":["plugins","policy","configuration"],"backgroundTag":"conflicting-plugin-policy","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"}