hashicorp/nomad · error

Invalid variable capability

Error message

Invalid variable capability '%s' in namespace %s

What it means

Returned by acl.Parse in acl/policy.go:607 when a variables path block lists a capability not accepted by isPathCapabilityValid (acl/policy.go:314). Variables path capabilities are limited to write, read, list, destroy, and deny; any other string (including namespace capabilities like submit-job) is rejected.

Solutions

  1. Use only read, write, list, destroy, or deny in variables path capabilities (exact lowercase)
  2. Replace 'delete' with 'destroy' (most common confusion)
  3. Move non-variable capabilities into the namespace stanza's capabilities list
  4. Pre-validate each capability against the fixed set of five before calling Parse

Example fix

// before
variables {
  path "secret/app" {
    capabilities = ["read", "delete"]
  }
}
// after
variables {
  path "secret/app" {
    capabilities = ["read", "destroy"]
  }
}
Defensive patterns

Strategy: validation

Validate before calling

var validVarCaps = map[string]bool{"read": true, "write": true, "list": true, "destroy": true, "deny": true}
for _, ns := range policy.Namespaces {
    if ns.Variables == nil { continue }
    for _, p := range ns.Variables.Paths {
        for _, c := range p.Capabilities {
            if !validVarCaps[c] { return fmt.Errorf("bad variables capability %q", c) }
        }
    }
}

Prevention

When it happens

Trigger: Calling acl.Parse with path { capabilities = [...] } in a variables stanza containing values outside {read, write, list, destroy, deny} — misspellings, uppercase forms, or namespace/node-pool capabilities pasted into a variables path.

Common situations: Copy-pasting namespace capabilities into variables stanzas; typos like 'delete' (correct word is 'destroy'); uppercase 'READ'; generators reusing a generic capability enum.

Related errors


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

Appendix: source

Thrown at acl/policy.go:607

		// Expand implicit capabilities
		expandNamespaceCapabilities(ns)

		if ns.Variables != nil {
			if len(ns.Variables.Paths) == 0 {
				return nil, fmt.Errorf("Invalid variable policy: no variable paths in namespace %s", ns.Name)
			}
			for _, pathPolicy := range ns.Variables.Paths {
				if pathPolicy.PathSpec == "" {
					return nil, fmt.Errorf("Invalid missing variable path in namespace %s", ns.Name)
				}
				if strings.HasPrefix(pathPolicy.PathSpec, "/") {
					return nil, fmt.Errorf(
						"Invalid variable path %q in namespace %s: cannot start with a leading '/'`",
						pathPolicy.PathSpec, ns.Name)
				}
				for _, cap := range pathPolicy.Capabilities {
					if !isPathCapabilityValid(cap) {
						return nil, fmt.Errorf(
							"Invalid variable capability '%s' in namespace %s", cap, ns.Name)
					}
				}
				pathPolicy.Capabilities = expandVariablesCapabilities(pathPolicy.Capabilities)

			}
		}

		// Remove the namespace name from the extra key list.
		p.removeExtraKey(ns.Name)
	}

	for _, np := range p.NodePools {
		if !validNodePool.MatchString(np.Name) {
			return nil, fmt.Errorf("Invalid node pool name '%s'", np.Name)
		}
		if np.Policy != "" && !isPolicyValid(np.Policy) {
			return nil, fmt.Errorf("Invalid node pool policy '%s' for '%s'", np.Policy, np.Name)

View on GitHub (pinned to 482b49bf1a)