jax-ml/jax · error · NotImplementedError
Only unstack along the last dimension is supported in…
Error message
Only unstack along the last dimension is supported in Triton.
What it means
The Triton lowering of unstack is only implemented for the trailing (last) dimension, matching Triton's register layout. It fires when the unstack axis is not the final axis of the array.
Solutions
- Transpose first so the size-2 axis is last, unstack, then proceed
- Store the pair along the last dimension when constructing the tensor
- Use explicit indexing v[..., 0], v[..., 1] instead of unstack
Example fix
// before a, b = jax.lax.unstack(v, axis=0) # v shape [2, B] // after a, b = jax.lax.unstack(jnp.transpose(v), axis=-1)
Defensive patterns
Strategy: validation
Validate before calling
def safe_unstack(v, axis=-1):
if axis != v.ndim - 1:
v = jnp.moveaxis(v, axis, -1)
return jax.lax.unstack(v, axis=-1) Type guard
def last_axis_split_ok(axis, ndim) -> bool:
return axis == ndim - 1 Try / catch
try:
parts = jax.lax.unstack(v, axis=axis)
except NotImplementedError:
parts = jax.lax.unstack(jnp.moveaxis(v, axis, -1), axis=-1) Prevention
- Always unstack along the last axis in Triton Pallas kernels
- moveaxis before unstack when data layout differs
- Prefer explicit indexing for one-off extraction
When it happens
Trigger: jax.lax.unstack(v, axis=k) with k != v.ndim - 1 inside a Triton Pallas kernel, e.g. unstacking a [2, B] tensor along axis 0.
Common situations: Unstacking paired values stored along the leading axis; code written against TPU Pallas where any axis works.
Related errors
- Only concatenate along the last dimension is supported.
- Only stack along the last dimension is supported in Triton.
- Only unstack of size 2 is supported in Triton.
- cannot cast to
- loading from a block pointer is not supported
AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27).
Data as JSON: /api/errors/58267fe522f4f4dc.
Report an issue: GitHub.
Appendix: source
Thrown at jax/_src/pallas/triton/lowering.py:1860
x = _ensure_ir_value(x, x_aval)
y = _ensure_ir_value(y, y_aval)
ty = ir.RankedTensorType(x.type)
shape = list(ty.shape)
shape.append(2)
ret_type = ir.RankedTensorType.get(shape, ty.element_type, ty.encoding)
return tt_dialect.join(ret_type, x, y)
@register_lowering(jax._src.lax.lax.unstack_p)
def _unstack_lowering_rule(ctx: LoweringRuleContext, x, *, axis):
[x_aval] = ctx.avals_in
if x_aval.shape[axis] != 2:
raise NotImplementedError("Only unstack of size 2 is supported in Triton.")
if axis != x_aval.ndim - 1:
raise NotImplementedError("Only unstack along the last dimension is supported in Triton.")
x = _ensure_ir_value(x, x_aval)
return tuple(tt_dialect.split(x))
@register_lowering(lax.split_p)
def _split_lowering_rule(ctx: LoweringRuleContext, x, *, sizes, axis):
pass
# TODO(cjfj): Add support for larger powers of 2.
num_parts = len(sizes)
if num_parts != pallas_utils.next_power_of_2(num_parts):
raise NotImplementedError("Only power-of-2 num parts supported.")
if any(size != sizes[0] for size in sizes):
raise NotImplementedError("Only equal-sized splits are supported.")
def split_into_2(x):
shape = ir.RankedTensorType(x.type).shape
x = _reshape(x, shape[:axis] + [2, shape[axis] // 2] + shape[axis + 1 :])View on GitHub (pinned to 1e1c6a8fc0)