hashicorp/nomad · error

Invalid operator policy: %#v

Error message

Invalid operator policy: %#v

What it means

acl.Parse (acl/policy.go:688) validates the top-level operator block's Policy string with isPolicyValid, which only accepts deny, read, write, scale. A non-empty operator policy with any other value fails parsing and rejects the whole policy. The OperatorPolicy struct is printed via %#v.

Source

Thrown at acl/policy.go:688

	// with the unknown keys.
	if len(p.ExtraKeysHCL) > 0 && strict {
		return nil, fmt.Errorf("Invalid or duplicate policy keys: %v",
			strings.Join(p.ExtraKeysHCL, ", "))
	}

	p.ExtraKeysHCL = nil

	if p.Agent != nil && !isPolicyValid(p.Agent.Policy) {
		return nil, fmt.Errorf("Invalid agent policy: %#v", p.Agent)
	}

	if p.Node != nil && !isPolicyValid(p.Node.Policy) {
		return nil, fmt.Errorf("Invalid node policy: %#v", p.Node)
	}

	if p.Operator != nil {
		if p.Operator.Policy != "" && !isPolicyValid(p.Operator.Policy) {
			return nil, fmt.Errorf("Invalid operator policy: %#v", p.Operator)
		}
		for _, cap := range p.Operator.Capabilities {
			if !isOperatorCapabilityValid(cap) {
				return nil, fmt.Errorf("Invalid operator capability '%s'", cap)
			}
		}

		// Expand the short hand policy to the capabilities and
		// add to any existing capabilities
		if p.Operator.Policy != "" {
			extraCap := expandOperatorPolicy(p.Operator.Policy)
			p.Operator.Capabilities = append(p.Operator.Capabilities, extraCap...)
		}
	}

	if p.Sentinel != nil {
		if p.Sentinel.Policy != "" && !isPolicyValid(p.Sentinel.Policy) {
			return nil, fmt.Errorf("Invalid sentinel policy: %#v", p.Sentinel)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set policy to one of exactly: deny, read, write, scale (lowercase).
  2. For granular operator permissions, drop policy and use capabilities = ["snapshot-save", "keyring-read", ...] instead.
  3. Check the %#v struct in the error to identify the bad value.

Example fix

// before
operator {
  policy = "snapshot"
}
// after
operator {
  capabilities = ["snapshot-save"]
}
Defensive patterns

Strategy: validation

Validate before calling

if policy.Operator != nil && policy.Operator.Policy != "" &&
    !map[string]bool{"deny": true, "read": true, "write": true, "scale": true}[policy.Operator.Policy] {
    return fmt.Errorf("operator policy %q must be deny|read|write|scale", policy.Operator.Policy)
}

Type guard

func isPolicyValid(policy string) bool {
    switch policy {
    case "deny", "read", "write", "scale":
        return true
    }
    return false
}

Try / catch

_, err := acl.Parse(rules, acl.PolicyParseStrict)
if err != nil {
    if strings.Contains(err.Error(), "Invalid operator policy") {
        // replace the operator shorthand policy or switch to capabilities
    }
    return err
}

Prevention

When it happens

Trigger: acl.Parse on a policy containing operator { policy = "<invalid>" } where the value is not exactly "deny", "read", "write", or "scale" — e.g. 'snapshot', 'keyring', or an empty quoted string.

Common situations: Putting operator capability names (snapshot-save, keyring-rotate, etc.) into the policy field instead of the capabilities list; case mistakes; template generation mixing policy and capability vocabularies.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/1b136cd0f7e475ca. Report an issue: GitHub.