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 Services

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Move the constraint block from the task to the enclosing group block.
  2. If per-task distinction is needed, use group-level distinct_property with ${meta.*} attributes or the spread stanza.
  3. 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

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


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