jax-ml/jax · error · InconclusiveDimensionOperation

Symbolic dimension '{self}' used in a context that requires

Error message

Symbolic dimension '{self}' used in a context that requires a constant

What it means

Python calls int(x) when an object is used where a plain integer is required (e.g. range(dim), list slicing, building shapes for non-JAX APIs). A symbolic dimension has no known integer value, so it cannot be converted, raising InconclusiveDimensionOperation.

Source

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

  def __rmod__(self, dividend):
    if isinstance(dividend, core.Tracer) or not _convertible_to_poly(dividend):
      return self.__jax_array__().__rmod__(dividend)
    return _ensure_poly(dividend, "mod", self.scope).__mod__(self)

  def __divmod__(self, divisor):
    if isinstance(divisor, core.Tracer) or not _convertible_to_poly(divisor):
      return self.__jax_array__().__divmod__(divisor)
    return self._divmod(divisor)

  def __rdivmod__(self, dividend):
    if isinstance(dividend, core.Tracer) or not _convertible_to_poly(dividend):
      return self.__jax_array__().__rdivmod__(dividend)
    return _ensure_poly(dividend, "divmod", self.scope).__divmod__(self)

  def __int__(self):
    if (c := _DimExpr._to_constant(self)) is not None:
      return c
    raise InconclusiveDimensionOperation(f"Symbolic dimension '{self}' used in a context that requires a constant")

  # We must overload __eq__ and __ne__, or else we get unsound defaults.
  def __eq__(self, other: Any) -> bool:
    if isinstance(other, type(self)):
      if self.scope is not other.scope:
        return False
    elif not core.is_constant_dim(other):
      return False

    # Equality is used very frequently because expressions are cached. We could
    # implement a more precise version based on `(self - other).bounds() = (0, 0)`
    # but that would be too expensive. It would also have the unfortunate drawback
    # that we cannot then cache `e.bounds()` because hashing invokes equality
    # which would lead to infinite recursion.
    diff = self - other

    # We look for `self - other == k`, and we rely on the fact that when we
    # normalize _DimExpr that represent integers as ints.

View on GitHub (pinned to 1e1c6a8fc0)

Solutions

  1. Replace Python constructs with JAX equivalents (lax.iota / jnp.arange instead of range; jnp.zeros instead of np.zeros)
  2. Move the int() usage outside the traced function or make that dimension concrete
  3. Use jnp operations that accept traced values instead of host-side ints

Example fix

# before
for i in range(n): acc += x[i]  # n symbolic
# after
acc = jnp.sum(x, axis=0)  # or use vmap/lax.fori_loop with traced bounds
Defensive patterns

Strategy: type-guard

Validate before calling

from jax._src.export.shape_poly import _DimExpr, InconclusiveDimensionOperation
if isinstance(d, _DimExpr): use_jnp_ops(d)  # never int(d)

Type guard

def needs_constant(d) -> bool:
    return isinstance(d, (int,)) and not hasattr(d, '_factors')

Try / catch

from jax._src.export import shape_poly
try:
    n = int(dim)
except shape_poly.InconclusiveDimensionOperation:
    n = None  # switch to jnp.arange/lax equivalents

Prevention

When it happens

Trigger: range(symbolic_dim), np.zeros((dim,)), or any int(dim)/indexing use while tracing with polymorphic shapes under jax.export.

Common situations: Porting code to shape-polymorphic export where a batch dimension is used in Python-level loops, Python slicing with symbolic bounds, or passed to numpy/stdlib functions.

Related errors


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