hashicorp/nomad · error
Invalid sentinel policy: %#v
Error message
Invalid sentinel policy: %#v
What it means
acl.Parse (acl/policy.go:706) validates the top-level sentinel block's Policy string with isPolicyValid, which only accepts deny, read, write, scale. A non-empty sentinel policy with any other value fails parsing and rejects the whole policy. Sentinel blocks are Nomad Enterprise features, so this typically surfaces in enterprise policy files.
Source
Thrown at acl/policy.go:706
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)
}
for _, cap := range p.Sentinel.Capabilities {
if !isSentinelCapabilityValid(cap) {
return nil, fmt.Errorf("Invalid sentinel capability '%s'", cap)
}
}
// Expand the short hand policy to the capabilities and
// add to any existing capabilities
if p.Sentinel.Policy != "" {
extraCap := expandSentinelPolicy(p.Sentinel.Policy)
p.Sentinel.Capabilities = append(p.Sentinel.Capabilities, extraCap...)
}
}
if p.Quota != nil && !isPolicyValid(p.Quota.Policy) {
return nil, fmt.Errorf("Invalid quota policy: %#v", p.Quota)
}View on GitHub (pinned to 482b49bf1a)
Solutions
- Set policy to one of exactly: deny, read, write, scale (lowercase).
- For granular Sentinel permissions, use capabilities = ["sentinel-read", "sentinel-submit", "sentinel-delete"] instead of the shorthand.
- Check the %#v struct in the error to identify the offending value.
Example fix
// before
sentinel {
policy = "submit"
}
// after
sentinel {
capabilities = ["sentinel-submit"]
} Defensive patterns
Strategy: validation
Validate before calling
if policy.Sentinel != nil && policy.Sentinel.Policy != "" &&
!map[string]bool{"deny": true, "read": true, "write": true, "scale": true}[policy.Sentinel.Policy] {
return fmt.Errorf("sentinel policy %q must be deny|read|write|scale", policy.Sentinel.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 sentinel policy") {
// replace the sentinel shorthand policy or switch to capabilities
}
return err
} Prevention
- Use only deny, read, write, scale in the sentinel policy field
- Use capabilities = ["sentinel-..."] for submit/delete grants instead of the policy shorthand
- Note sentinel blocks require Nomad Enterprise — validate the target edition supports them
When it happens
Trigger: acl.Parse on a policy containing sentinel { policy = "<invalid>" } where the value is not exactly "deny", "read", "write", or "scale" — e.g. 'submit', 'sentinel-read', or an empty string set by a template.
Common situations: Putting sentinel capability names (sentinel-submit, sentinel-delete) into the policy field instead of the capabilities list; case mistakes; OSS Nomad users pasting an enterprise example policy verbatim with modified values.
Related errors
- Invalid sentinel capability '%s'
- Invalid namespace name: %#v
- Invalid namespace policy: %#v
- Invalid namespace capability '%s': %#v
- Invalid variable policy: no variable paths in namespace %s
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/25e24621da2dde0b.
Report an issue: GitHub.