larksuite/cli · error · ErrMultipleRestricts

%w: %v

Error message

%w: %v

What it means

cmdpolicy.Resolve returns this error, wrapping ErrMultipleRestricts, when rules arrive from more than one DISTINCT plugin. Independent plugins cannot silently widen each other's policy, so the resolver deliberately aborts instead of merging. Note: multiple rules from a single plugin are fine; only multiple distinct plugin owners conflict.

Source

Thrown at internal/cmdpolicy/resolver.go:54

	YAMLRules   []*platform.Rule
	YAMLPath    string
}

var ErrMultipleRestricts = errors.New("multiple plugins called Restrict; only one plugin may own the policy")

// Resolve picks by precedence: plugin > yaml > none, returning the full
// rule set the winning source contributes. Pure function; load yaml via
// LoadYAMLPolicy first. Every returned rule is validated.
//
// Multi-rule semantics (single owner): one plugin may contribute several
// rules (each a scoped grant, OR-combined by the engine), but two or more
// DISTINCT plugins contributing rules is still a configuration error --
// the resolver aborts so independent plugins cannot silently widen each
// other's policy. yaml may likewise carry several rules under "rules:".
func Resolve(s Sources) ([]*platform.Rule, ResolveSource, error) {
	owners := distinctOwners(s.PluginRules)
	if len(owners) > 1 {
		return nil, ResolveSource{}, fmt.Errorf("%w: %v", ErrMultipleRestricts, owners)
	}

	if len(s.PluginRules) > 0 {
		rules := make([]*platform.Rule, 0, len(s.PluginRules))
		for _, pr := range s.PluginRules {
			if err := ValidateRule(pr.Rule); err != nil {
				return nil, ResolveSource{}, fmt.Errorf("plugin %q rule invalid: %w", pr.PluginName, err)
			}
			rules = append(rules, pr.Rule)
		}
		return rules, ResolveSource{Kind: SourcePlugin, Name: owners[0]}, nil
	}

	if len(s.YAMLRules) > 0 {
		for _, r := range s.YAMLRules {
			if err := ValidateRule(r); err != nil {
				return nil, ResolveSource{}, fmt.Errorf("policy yaml %q: %w", s.YAMLPath, err)
			}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Remove or disable all but one plugin that contributes policy rules
  2. Consolidate the restrict rules of the plugins into a single plugin's rule set
  3. Move shared restrictions into the policy YAML (s.YAMLRules) instead of a second plugin, since a single YAML source is allowed alongside one plugin
  4. Check plugin registration/config to find the conflicting plugin names listed in the error

Example fix

// before: two plugins register rules
policy.Resolve(cmdpolicy.Sources{PluginRules: rulesFromPluginA + rulesFromPluginB})
// after: single plugin owner (or move extra rules to YAML)
policy.Resolve(cmdpolicy.Sources{PluginRules: rulesFromPluginA, YAMLRules: yamlRules, YAMLPath: "policy.yaml"})
Defensive patterns

Strategy: validation

Validate before calling

owners := map[string]bool{}
for _, pr := range pluginRules { owners[pr.PluginName] = true }
if len(owners) > 1 {
    return fmt.Errorf("refusing to resolve: multiple policy plugins: %v", owners)
}

Prevention

When it happens

Trigger: Applying user policy pruning where two or more different plugins each contributed PluginRules entries to Sources — e.g. loading several plugins that both register restrict rules.

Common situations: Installing multiple policy-enforcing plugins side by side, upgrading one plugin while an old policy plugin remains enabled, or composing plugin sets from different sources that both constrain commands.

Related errors


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