hashicorp/nomad · error

Version constraint is invalid: %v

Error message

Version constraint is invalid: %v

What it means

Nomad job constraint validation failed because the constraint's RTarget is not a parseable version constraint expression. Nomad uses hashicorp/go-version's NewConstraint to parse expressions like ">= 1.2, < 2.0". This error is collected into a multi-error and returned when Copy/Validate of a Constraint is called during job validation, so the job submission is rejected.

Source

Thrown at nomad/structs/structs.go:10078

	// requireLtarget specifies whether the constraint requires an LTarget to be
	// provided.
	requireLtarget := true

	// Perform additional validation based on operand
	switch c.Operand {
	case ConstraintDistinctHosts:
		requireLtarget = false
	case ConstraintSetContainsAll, ConstraintSetContainsAny, ConstraintSetContains:
		if c.RTarget == "" {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Set contains constraint requires an RTarget"))
		}
	case ConstraintRegex:
		if _, err := regexp.Compile(c.RTarget); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Regular expression failed to compile: %v", err))
		}
	case ConstraintVersion:
		if _, err := version.NewConstraint(c.RTarget); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Version constraint is invalid: %v", err))
		}
	case ConstraintSemver:
		if _, err := semver.NewConstraint(c.RTarget); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Semver constraint is invalid: %v", err))
		}
	case ConstraintDistinctProperty:
		// If a count is set, make sure it is convertible to a uint64
		if c.RTarget != "" {
			count, err := strconv.ParseUint(c.RTarget, 10, 64)
			if err != nil {
				mErr.Errors = append(mErr.Errors, fmt.Errorf("Failed to convert RTarget %q to uint64: %v", c.RTarget, err))
			} else if count < 1 {
				mErr.Errors = append(mErr.Errors, fmt.Errorf("Distinct Property must have an allowed count of 1 or greater: %d < 1", count))
			}
		}
	case ConstraintAttributeIsSet, ConstraintAttributeIsNotSet:
		if c.RTarget != "" {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Operator %q does not support an RTarget", c.Operand))

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Fix the constraint RTarget to valid go-version syntax, e.g. ">= 1.2.0" or ">= 1.2, < 2.0".
  2. Pre-validate the expression in Go with version.NewConstraint(target) before submitting the job.
  3. If you need SemVer-only semantics (prerelease support), use operand "semver" instead of "version".

Example fix

// before
constraint {
  attribute = "${attr.kernel.version}"
  version   = ">=1.2.x"
}
// after
constraint {
  attribute = "${attr.kernel.version}"
  version   = ">= 1.2.0"
}
Defensive patterns

Strategy: validation

Validate before calling

import "github.com/hashicorp/go-version"

func validVersionConstraint(target string) bool {
  _, err := version.NewConstraint(target)
  return err == nil
}
// call before submitting: validVersionConstraint(c.RTarget)

Try / catch

if _, err := version.NewConstraint(c.RTarget); err != nil {
  return fmt.Errorf("invalid version constraint %q: %w", c.RTarget, err)
}

Prevention

When it happens

Trigger: Submitting a job whose constraint has operand "version" and an RTarget that version.NewConstraint cannot parse, e.g. RTarget = ">=1.2.3-beta!" or "1..2" or empty-but-set garbage like "latest".

Common situations: Typo in the version expression in an HCL/JSON job spec; pasting a SemVer 2 prerelease expression into a version constraint (which only supports go-version syntax); upgrading Nomad with a stricter parser; programmatic job generation concatenating versions incorrectly.

Related errors


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