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

  1. Set policy to one of exactly: deny, read, write, scale (lowercase).
  2. For granular Sentinel permissions, use capabilities = ["sentinel-read", "sentinel-submit", "sentinel-delete"] instead of the shorthand.
  3. 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

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


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