hashicorp/nomad · error

Malformed constraint: %s

Error message

Malformed constraint: %s

What it means

parseSingle in the semver constraints package fails when a single constraint string does not match constraintRegexp, i.e. it is not of the form <operator><whitespace?><version>. The whole NewConstraint call returns this error without parsing anything. It is a pure input-syntax error on the caller-supplied constraint string.

Source

Thrown at helper/constraints/semver/constraints.go:109

		csStr[i] = c.String()
	}

	return strings.Join(csStr, ",")
}

// Check tests if a constraint is validated by the given version.
func (c *Constraint) Check(v *version.Version) bool {
	return c.f(v, c.check)
}

func (c *Constraint) String() string {
	return c.original
}

func parseSingle(v string) (*Constraint, error) {
	matches := constraintRegexp.FindStringSubmatch(v)
	if matches == nil {
		return nil, fmt.Errorf("Malformed constraint: %s", v)
	}

	check, err := version.NewSemver(matches[2])
	if err != nil {
		return nil, err
	}

	return &Constraint{
		f:        constraintOperators[matches[1]],
		check:    check,
		original: v,
	}, nil
}

//-------------------------------------------------------------------
// Constraint functions
//-------------------------------------------------------------------

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Inspect the %s in the message to find the offending token and correct its syntax: operator (>=, >, <, <=, =, ~>) immediately followed by a valid semver.
  2. Validate the constraint string with a regex or NewConstraint in a dry-run before persisting or using it in config.
  3. Check that version components are numeric and the version parses (cross-check with version.NewSemver).
  4. Sanitize user-supplied constraint input and reject empty tokens before calling NewConstraint.

Example fix

// before
c, err := constraints.NewConstraint("~> 1.2") // unsupported operator/version
// after
c, err := constraints.NewConstraint(">= 1.2.0, < 2.0.0")
if err != nil {
    return fmt.Errorf("invalid constraint %q: %w", raw, err)
}
Defensive patterns

Strategy: validation

Validate before calling

var constraintRe = regexp.MustCompile(`^(>=|<=|>|<|=|~>)?\s*v?\d+(\.\d+){0,2}([-+].*)?$`)
func validConstraint(s string) bool {
    s = strings.TrimSpace(s)
    if s == "" { return false }
    return constraintRe.MatchString(s)
}
// usage:
if !validConstraint(userInput) {
    return fmt.Errorf("invalid constraint %q", userInput)
}

Try / catch

c, err := constraints.NewConstraint(raw)
if err != nil {
    if strings.HasPrefix(err.Error(), "Malformed constraint") {
        return fmt.Errorf("constraint %q is not valid syntax: %w", raw, err)
    }
    return err
}

Prevention

When it happens

Trigger: NewConstraint("~> 1.2") or other operators unsupported by this regex; missing version (">= "); non-numeric versions (">= v1.x"); stray characters (">=1.0.0!"); splitting a compound constraint incorrectly so an empty or malformed token is parsed.

Common situations: Hand-written version pins in config files with typos; constraints copied from other ecosystems (e.g. Ruby's "~>" pessimistic operator) that hashicorp/go-version doesn't support; empty strings produced by splitting on ","; user-supplied constraint input not validated upstream.

Understand the failure class

Related errors


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