hashicorp/nomad · error
Constraint %d has disallowed Operand at task level: %s
Error message
Constraint %d has disallowed Operand at task level: %s
What it means
Emitted in Task validation (nomad/structs/structs.go:8293) when a task-level constraint uses the DistinctHosts or DistinctProperty operand. These placement-counting constraints are only meaningful at the task-group level, so Nomad rejects them when declared on an individual task.
Source
Thrown at nomad/structs/structs.go:8293
}
// Validate the log config
if t.LogConfig == nil {
mErr.Errors = append(mErr.Errors, errors.New("Missing Log Config"))
} else if err := t.LogConfig.Validate(tg.EphemeralDisk); err != nil {
mErr.Errors = append(mErr.Errors, err)
}
// Validate constraints and affinities.
for idx, constr := range t.Constraints {
if err := constr.Validate(); err != nil {
outer := fmt.Errorf("Constraint %d validation failed: %s", idx+1, err)
mErr.Errors = append(mErr.Errors, outer)
}
switch constr.Operand {
case ConstraintDistinctHosts, ConstraintDistinctProperty:
outer := fmt.Errorf("Constraint %d has disallowed Operand at task level: %s", idx+1, constr.Operand)
mErr.Errors = append(mErr.Errors, outer)
}
}
if jobType == JobTypeSystem {
if t.Affinities != nil {
mErr.Errors = append(mErr.Errors, fmt.Errorf("System jobs may not have an affinity block"))
}
} else {
for idx, affinity := range t.Affinities {
if err := affinity.Validate(); err != nil {
outer := fmt.Errorf("Affinity %d validation failed: %s", idx+1, err)
mErr.Errors = append(mErr.Errors, outer)
}
}
}
// Validate ServicesView on GitHub (pinned to 482b49bf1a)
Solutions
- Move the constraint block from the task to the enclosing group block.
- If per-task distinction is needed, use group-level distinct_property with ${meta.*} attributes or the spread stanza.
- Validate with nomad job validate after moving the block.
Example fix
// before
task "app" {
constraint { operand = "distinct_hosts" }
}
// after
group "g" {
constraint { operand = "distinct_hosts" }
task "app" {}
} Defensive patterns
Strategy: validation
Validate before calling
for i, c := range t.Constraints {
if c.Operand == "distinct_hosts" || c.Operand == "distinct_property" {
return fmt.Errorf("constraint %d: %s not allowed at task level; move to group", i+1, c.Operand)
}
} Type guard
func isGroupOnlyOperand(op string) bool {
return op == "distinct_hosts" || op == "distinct_property"
} Prevention
- Declare distinct_hosts/distinct_property only inside group blocks.
- Use spread stanzas for fine-grained distribution.
- Lint job files for operand placement.
When it happens
Trigger: task { constraint { operand = "distinct_hosts" } } or operand = "distinct_property" inside a task block; job submission or nomad job validate of that job.
Common situations: Users copy a group-level constraint into a task thinking it applies per task; misunderstanding Nomad's placement semantics where spread/distinct constraints are group-scoped.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- node %s is not feasible for volume
- no node meets constraints: %d nodes had existing volume, %d
- invalid constraint: %v
- missing secret ID
- namespace cannot contain template delimiters or parenthesis
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/05de479f197040ee.
Report an issue: GitHub.