hashicorp/nomad · error

Semver affinity is invalid: %v

Error message

Semver affinity is invalid: %v

What it means

This error comes from Affinity.Validate() in nomad/structs/structs.go. When an affinity uses the 'semver' operator (ConstraintSemver), Nomad parses the affinity's RTarget with hashicorp/go-version's semver.NewConstraint. If the target string is not a valid semver constraint, validation fails with 'Semver affinity is invalid: <parse error>'. It exists to catch malformed version constraints at job-submission time rather than at scheduling time.

Source

Thrown at nomad/structs/structs.go:10216

	}

	// Perform additional validation based on operand
	switch a.Operand {
	case ConstraintSetContainsAll, ConstraintSetContainsAny, ConstraintSetContains:
		if a.RTarget == "" {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Set contains operators require an RTarget"))
		}
	case ConstraintRegex:
		if _, err := regexp.Compile(a.RTarget); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Regular expression failed to compile: %v", err))
		}
	case ConstraintVersion:
		if _, err := version.NewConstraint(a.RTarget); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Version affinity is invalid: %v", err))
		}
	case ConstraintSemver:
		if _, err := semver.NewConstraint(a.RTarget); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Semver affinity is invalid: %v", err))
		}
	case "=", "==", "is", "!=", "not", "<", "<=", ">", ">=":
		if a.RTarget == "" {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Operator %q requires an RTarget", a.Operand))
		}
	default:
		mErr.Errors = append(mErr.Errors, fmt.Errorf("Unknown affinity operator %q", a.Operand))
	}

	// Ensure we have an LTarget
	if a.LTarget == "" {
		mErr.Errors = append(mErr.Errors, fmt.Errorf("No LTarget provided but is required"))
	}

	// Ensure that weight is between -100 and 100, and not zero
	if a.Weight == 0 {
		mErr.Errors = append(mErr.Errors, fmt.Errorf("Affinity weight cannot be zero"))
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Fix the RTarget to be a valid semver constraint accepted by hashicorp/go-version's semver.NewConstraint (e.g. "~> 1.2.0", ">= 1.2, < 2.0").
  2. If you need non-semver range syntax (like "1.x"), use the "version" operator instead of "semver".
  3. Ensure RTarget is non-empty when operand is "semver".

Example fix

// before
affinity {
  attribute = "attr.meta.app_version"
  operator  = "semver"
  value     = "1.x"
}
// after
affinity {
  attribute = "attr.meta.app_version"
  operator  = "semver"
  value     = "~> 1.0"
}
Defensive patterns

Strategy: validation

Validate before calling

func validSemverConstraint(s string) bool { _, err := semver.NewConstraint(s); return err == nil }
// before submit: check every affinity with operand "semver"

Type guard

func isSemverOperand(op string) bool { return op == "semver" }

Prevention

When it happens

Trigger: Submitting a job (via nomad job run / /v1/jobs API) whose task/group affinity has operand "semver" and an RTarget that fails semver.NewConstraint, e.g. "~> 2" with invalid spacing, "1.x", "latest", or an empty RTarget.

Common situations: Copy-pasting npm-style version ranges (which go-version's semver parser does not accept), typos like "v1.2..3", forgetting to quote ranges in HCL, or switching an affinity from "version" to "semver" without adjusting the constraint syntax.

Related errors


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