jax-ml/jax · error · ValueError

Unsatisfiable explicit constraint: {constr.debug_str}

Error message

Unsatisfiable explicit constraint: {constr.debug_str}

What it means

After parsing, JAX evaluates the constant difference e1-e2 of a constraint. If both sides are known constants and the constraint is false (e.g. '3 == 4' or '2 >= 5'), it is unsatisfiable and rejected immediately.

Source

Thrown at jax/_src/export/shape_poly.py:1071

      if cmp_pos < 0:
        raise ValueError("Constraint parsing error: must contain one of '==' or '>=' or '<='")
    e1_str = c_str[:cmp_pos]
    e1, = _Parser(e1_str, None, repr(e1_str), self).parse()
    e2_str = c_str[cmp_pos + 2:]
    e2, = _Parser(e2_str, None, repr(e2_str), self).parse()
    if cmp == Comparator.GEQ and not is_geq:
      e1, e2 = e2, e1

    # Compute e1 - e2 before we add to normalization rules
    constr = _SymbolicConstraint(debug_str=c_str, cmp=cmp, e1=e1, e2=e2,
                                 diff=e1 - e2)
    self._process_explicit_constraint(constr)

  def _process_explicit_constraint(self, constr: _SymbolicConstraint):
    if (diff_const := _DimExpr._to_constant(constr.diff)) is not None:
      if ((constr.cmp == Comparator.EQ and diff_const != 0) or
          (constr.cmp == Comparator.GEQ and diff_const < 0)):
        raise ValueError(f"Unsatisfiable explicit constraint: {constr.debug_str}")
      return

    if constr.cmp == Comparator.EQ:
      if not isinstance(constr.e1, _DimExpr):
        raise ValueError("Invalid equality constraint: {e1} == {e2}. "
                         "The left-hand-side must be of the form `term * coefficient`.")
      (before, before_k), *rest = constr.e1._sorted_terms
      if rest:
        raise ValueError("Invalid equality constraint: {e1} == {e2}. "
                         "The left-hand-side must be of the form `term * coefficient`.")

      after = _ensure_poly(constr.e2, "parse_constraint", constr.e1.scope)
      if before in self._normalization_rules:
        raise NotImplementedError(
            f"Found multiple equality constraints with the same left-hand-side: {before}")
      self._normalization_rules[before] = (after, before_k)
      # Look for constraints of the form mod(before_e1, before_k2) * 1 == 0
      if (before_k == 1 and

View on GitHub (pinned to 1e1c6a8fc0)

Solutions

  1. Fix the arithmetic or comparator direction so the constant constraint holds
  2. If the operands were meant to be symbolic, check variable spelling (a typo makes both sides constant)
  3. Print/inspect the generated constraint strings before constructing the SymbolicScope

Example fix

# before
constraints = ('2 * 2 >= 5',)
# after
constraints = ('2 * 2 <= 5',)  # or fix the intended math
Defensive patterns

Strategy: validation

Validate before calling

# both sides constant and comparison false -> reject early
for c in constraints:
    if well_formed(c) and both_constant(c): assert eval_const_cmp(c)

Try / catch

try:
    scope = SymbolicScope(constraints)
except ValueError as e:
    if 'Unsatisfiable' in str(e): fix_constraint_arithmetic()

Prevention

When it happens

Trigger: Constraints between constants that contradict: '4 <= 3', '8 == 9', or expressions that constant-fold to a violated comparison, e.g. '2*2 >= 5'.

Common situations: Computed constraint strings where a variable was substituted by a wrong constant; inverted comparison direction by mistake.

Related errors


AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27). Data as JSON: /api/errors/3bc14f8039292dc2. Report an issue: GitHub.