hashicorp/nomad · error
Invalid sentinel capability '%s'
Error message
Invalid sentinel capability '%s'
What it means
acl.Parse (acl/policy.go:710) checks each entry of the sentinel block's Capabilities list with isSentinelCapabilityValid, which only accepts deny, sentinel-read, sentinel-submit, and sentinel-delete. Any other capability string fails parsing and rejects the whole policy.
Source
Thrown at acl/policy.go:710
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)
}
if p.Plugin != nil && !p.Plugin.isValid() {
return nil, fmt.Errorf("Invalid plugin policy: %#v", p.Plugin)
}View on GitHub (pinned to 482b49bf1a)
Solutions
- Replace the invalid capability with one of exactly: deny, sentinel-read, sentinel-submit, sentinel-delete.
- Alternatively remove capabilities and use shorthand policy = "read"|"write"|"deny" (write expands to sentinel-read + sentinel-submit + sentinel-delete).
- The '%s' in the error names the exact rejected capability — fix that entry.
Example fix
// before
sentinel {
capabilities = ["submit", "delete"]
}
// after
sentinel {
capabilities = ["sentinel-submit", "sentinel-delete"]
} Defensive patterns
Strategy: validation
Validate before calling
var validSentinelCaps = map[string]bool{"deny": true, "sentinel-read": true,
"sentinel-submit": true, "sentinel-delete": true}
if policy.Sentinel != nil {
for _, c := range policy.Sentinel.Capabilities {
if !validSentinelCaps[c] {
return fmt.Errorf("invalid sentinel capability %q", c)
}
}
} Type guard
func isSentinelCapabilityValid(cap string) bool {
switch cap {
case "deny", "sentinel-read", "sentinel-submit", "sentinel-delete":
return true
}
return false
} Try / catch
_, err := acl.Parse(rules, acl.PolicyParseStrict)
if err != nil {
if strings.Contains(err.Error(), "Invalid sentinel capability") {
// prefix bare names with 'sentinel-' or use the policy shorthand
}
return err
} Prevention
- Always include the 'sentinel-' prefix: sentinel-read, sentinel-submit, sentinel-delete
- There is no 'sentinel-write'; use policy = "write" or enumerate submit+delete
- Copy capability constants from acl.SentinelCapability* rather than typing them
When it happens
Trigger: acl.Parse on a policy where sentinel { capabilities = [...] } contains an unrecognized string such as 'read', 'submit', 'sentinel-write', or a namespace capability name.
Common situations: Dropping the 'sentinel-' prefix (writing 'read'/'submit' instead of 'sentinel-read'/'sentinel-submit'); assuming a 'sentinel-write' exists; generating policies from generic ACL templates that don't know the sentinel capability vocabulary.
Related errors
- Invalid sentinel policy: %#v
- Invalid host volume capability '%s': %#v
- Invalid operator capability '%s'
- ErrMultipleNamespaces
- Feature "Node Pools Governance" is unlicensed
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/bb1c4d1362f94b44.
Report an issue: GitHub.