{"record":{"id":"1416f4b4ff2d7e81","repo":"jax-ml/jax","slug":"end-must-be-greater-than-start-but-got-end","errorCode":null,"errorMessage":"end must be greater than start, but got: {end} <= {start}","messagePattern":"end must be greater than start, but got: (.+?) <= (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"jax/_src/pallas/triton/lowering.py","lineNumber":1498,"sourceCode":"                        sharding):\n  iota = _make_range(0, shape[dimension])\n  iota = _cast(iota, jnp.int32, dtype)\n  for i in range(len(shape)):\n    if i != dimension:\n      iota = _expand_dims(iota, i)\n  return _bcast_to(iota, shape)\n\n\ndef _element_type(t: ir.Type) -> ir.Type:\n  if isinstance(t, ir.RankedTensorType):\n    return ir.RankedTensorType(t).element_type\n  else:\n    return t\n\n\ndef _make_range(start: int, end: int) -> ir.Value:\n  if end <= start:\n    raise ValueError(\n        f\"end must be greater than start, but got: {end} <= {start}\"\n    )\n  if max(start, end) >= 2**32:\n    raise ValueError(\"start and end must fit in int32\")\n  return tt_dialect.make_range(\n      ir.RankedTensorType.get([end - start], ir.IntegerType.get_signless(32)),\n      start,\n      end,\n  )\n\n\ndef _full(t: ir.Type, v: Any) -> ir.Value:\n  element_type = _element_type(t)\n  if isinstance(element_type, ir.IntegerType):\n    result = arith_dialect.constant(element_type, int(v))\n  elif isinstance(element_type, ir.FloatType):\n    result = arith_dialect.constant(element_type, float(v))\n  else:","sourceCodeStart":1480,"sourceCodeEnd":1516,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/_src/pallas/triton/lowering.py#L1480-L1516","documentation":"_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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Validate that every dimension used for iota/arange/indices is >= 1 and clamp or skip execution for size-0 inputs","Fix off-by-one arithmetic so end > start (e.g. arange(lo, hi) with hi > lo)","If size-0 kernels are legitimately needed, guard the kernel launch: skip grid steps where the block length would be 0"],"exampleFix":"// before\nidx = jnp.arange(start, end)  # end <= start crashes lowering\n// after\nif end > start:\n    idx = jnp.arange(start, end)\nelse:\n    idx = jnp.zeros((0,), dtype=jnp.int32)","handlingStrategy":"validation","validationCode":"def validate_ranges(n):\n    if not isinstance(n, int) or n < 1:\n        raise ValueError(f'dimension must be >= 1 for iota/arange on Triton, got {n}')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Skip kernel launches for size-0 dimensions rather than relying on empty blocks","Double-check slicing bounds so arange/loopy ranges always have end > start"],"tags":["jax","pallas","triton","arange","iota","empty-range","validation"],"backgroundTag":"empty-range-argument","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}