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
- Set policy to one of exactly: deny, read, write, scale (lowercase).
- For granular operator permissions, drop policy and use capabilities = ["snapshot-save", "keyring-read", ...] instead.
- 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
- Use only deny, read, write, scale in the operator policy field
- Put operation-specific grants (snapshot-save, keyring-*) in capabilities, not policy
- Keep policy-generator tooling aligned with the canonical shorthand constants
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
- Invalid namespace name: %#v
- Invalid namespace policy: %#v
- Invalid namespace capability '%s': %#v
- Invalid variable policy: no variable paths in namespace %s
- Invalid missing variable path in namespace %s
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/1b136cd0f7e475ca.
Report an issue: GitHub.