jax-ml/jax · error · ValueError

end must be greater than start, but got: {end} <= {start}

Error message

end must be greater than start, but got: {end} <= {start}

What it means

_make_range builds a Triton make_range op, which requires a strictly increasing (start, end) pair; end <= start is rejected because Triton ranges must be non-empty and ascending. It is hit from iota lowering, offset computation from BlockIdMapping/indices, and argreduce, all of which derive (start, end) from block sizes or index ranges.

Source

Thrown at jax/_src/pallas/triton/lowering.py:1498

                        sharding):
  iota = _make_range(0, shape[dimension])
  iota = _cast(iota, jnp.int32, dtype)
  for i in range(len(shape)):
    if i != dimension:
      iota = _expand_dims(iota, i)
  return _bcast_to(iota, shape)


def _element_type(t: ir.Type) -> ir.Type:
  if isinstance(t, ir.RankedTensorType):
    return ir.RankedTensorType(t).element_type
  else:
    return t


def _make_range(start: int, end: int) -> ir.Value:
  if end <= start:
    raise ValueError(
        f"end must be greater than start, but got: {end} <= {start}"
    )
  if max(start, end) >= 2**32:
    raise ValueError("start and end must fit in int32")
  return tt_dialect.make_range(
      ir.RankedTensorType.get([end - start], ir.IntegerType.get_signless(32)),
      start,
      end,
  )


def _full(t: ir.Type, v: Any) -> ir.Value:
  element_type = _element_type(t)
  if isinstance(element_type, ir.IntegerType):
    result = arith_dialect.constant(element_type, int(v))
  elif isinstance(element_type, ir.FloatType):
    result = arith_dialect.constant(element_type, float(v))
  else:

View on GitHub (pinned to 1e1c6a8fc0)

Solutions

  1. Validate that every dimension used for iota/arange/indices is >= 1 and clamp or skip execution for size-0 inputs
  2. Fix off-by-one arithmetic so end > start (e.g. arange(lo, hi) with hi > lo)
  3. If size-0 kernels are legitimately needed, guard the kernel launch: skip grid steps where the block length would be 0

Example fix

// before
idx = jnp.arange(start, end)  # end <= start crashes lowering
// after
if end > start:
    idx = jnp.arange(start, end)
else:
    idx = jnp.zeros((0,), dtype=jnp.int32)
Defensive patterns

Strategy: validation

Validate before calling

def validate_ranges(n):
    if not isinstance(n, int) or n < 1:
        raise ValueError(f'dimension must be >= 1 for iota/arange on Triton, got {n}')

Prevention

When it happens

Trigger: A Pallas kernel grid/block specification yields a zero or negative-length range: e.g. an iota over a dimension of size 0, a block shape containing 0, or argreduce/iota where the computed start offset is >= end. Calling jnp.arange(a, b) with b <= a inside a Triton-lowered kernel also maps here.

Common situations: Dynamically computed block shapes that become 0 for degenerate inputs (empty batch); off-by-one bugs when slicing; passing a block dimension of 0 in pallas.BlockSpec; edge-case tests with size-0 arrays.

Related errors


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