sgl-project/sglang · error · TypeError
mlx_call_multi operation must return a non-empty tuple or li
Error message
mlx_call_multi operation must return a non-empty tuple or list of MLX arrays
What it means
The operation callback passed to mlx_call_multi must return a non-empty tuple or list of results (single outputs belong to mlx_call). After invoking the callback, the wrapper checks the container type and emptiness before touching elements.
Source
Thrown at python/sglang/srt/utils/tensor_bridge.py:366
borrowed: tuple[Any, ...] = tuple(
(
tensor.array
if isinstance(tensor, MlxTensorView)
else _torch_to_mlx(tensor.detach(), copy=False, synchronize=False)
)
for tensor in tensors
)
if target_device.type == "cpu" and any(
array.dtype == mx.float64 for array in borrowed
):
with mx.stream(mx.cpu):
result = operation(*borrowed)
else:
result = operation(*borrowed)
if not isinstance(result, (tuple, list)) or not result:
raise TypeError(
"mlx_call_multi operation must return a non-empty tuple or list of MLX arrays"
)
arrays = tuple(result)
if any(not isinstance(array, mx.array) for array in arrays):
raise TypeError("mlx_call_multi outputs must be MLX arrays")
# Prepare all outputs before crossing the one shared MLX evaluation
# boundary. This is the key difference from calling mlx_to_torch in a
# loop, which would fence/evaluate every result separately.
arrays = tuple(_prepare_mlx_export(array, target_device, mx) for array in arrays)
mx.eval(*arrays)
# DLPack cannot represent negative strides. Materialize all such outputs
# together so even this safety path has one additional evaluation boundary
# rather than one boundary per result.
negative = tuple(_has_negative_stride(array) for array in arrays)
if any(negative):
materialized = []View on GitHub (pinned to 0132848349)
Solutions
- Return a tuple: `return (result,)` from the callback
- Use mlx_call for single-output operations
- Ensure every code path in the callback returns the container
Example fix
# before
def op(a, b):
return a + b # single array
outs = mlx_call_multi(op, [a, b])
# after
def op(a, b):
return (a + b, a - b)
outs = mlx_call_multi(op, [a, b]) Defensive patterns
Strategy: validation
Validate before calling
result = op(*inputs) assert isinstance(result, (tuple, list)) and len(result) > 0
Type guard
def is_valid_multi_result(r) -> bool:
return isinstance(r, (tuple, list)) and len(r) > 0 Prevention
- Always return tuples from multi-output ops, even single-element ones
- Use mlx_call for single-output operations
When it happens
Trigger: A callback returning None, a single mx.array, an empty list [], or any non-tuple/list object.
Common situations: Reusing a single-output op in mlx_call_multi without wrapping its return, or an op with a conditional early `return` path returning None.
Related errors
- mlx_call_multi outputs must be MLX arrays
- SGLANG_USE_MLX requires stable Torch 2.13.x and MLX >= 0.32.
- SGLANG_USE_MLX requires stable Torch 2.13.x and MLX >= 0.32.
- SGLANG_USE_MLX requires an available MLX Metal device
- MLX async runner does not support forward mode: {forward_mod
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/2af0b194e2cc86fb.
Report an issue: GitHub.