sgl-project/sglang · error · ValueError

MLX 0.32 does not support complex128; convert the Torch tens

Error message

MLX 0.32 does not support complex128; convert the Torch tensor to complex64 explicitly

What it means

When converting a CPU torch tensor to MLX, complex128 is rejected because MLX 0.32 has no complex128 dtype. The check happens on the CPU-tensor branch of _torch_to_mlx, which otherwise zero-copies or preserves dtype.

Source

Thrown at python/sglang/srt/utils/tensor_bridge.py:85

    *,
    copy: bool,
    synchronize: bool = True,
) -> mx.array:
    """Convert one tensor, optionally borrowing its MPS allocation."""
    mx = _mlx_core()
    tensor = tensor.detach()

    if tensor.device.type == "mps":
        if synchronize:
            # Torch and MLX do not share stream state on Metal.
            torch.mps.synchronize()
        return mx.asarray(tensor, copy=copy)
    if tensor.device.type == "cpu":
        # CPU tensors always get MLX-owned storage.  In particular, do not
        # expose a NumPy/memoryview alias whose lifetime is controlled by the
        # caller.
        if tensor.dtype == torch.complex128:
            raise ValueError(
                "MLX 0.32 does not support complex128; convert the Torch tensor "
                "to complex64 explicitly"
            )
        # MLX 0.32 does not support float64 on its default Metal stream.  Keep
        # the dtype by constructing this uncommon CPU value on the CPU stream
        # instead of silently downcasting it to float32.
        if tensor.dtype == torch.float64:
            with mx.stream(mx.cpu):
                return mx.array(tensor, dtype=mx.float64)
        return mx.array(tensor)
    raise ValueError(
        f"The MLX tensor bridge supports CPU and MPS tensors, got {tensor.device}"
    )


class MlxTensorView:
    """A lifetime-bound, zero-copy MLX view of a Torch MPS tensor.

View on GitHub (pinned to 0132848349)

Solutions

  1. Cast before bridging: t = t.to(torch.complex64), then torch_to_mlx(t)
  2. Use complex64 throughout the pipeline to avoid repeated casts

Example fix

# before
mx_t = torch_to_mlx(t)  # t is complex128 -> ValueError
# after
mx_t = torch_to_mlx(t.to(torch.complex64))
Defensive patterns

Strategy: type-guard

Validate before calling

if tensor.dtype == torch.complex128:
    tensor = tensor.to(torch.complex64)
mx_t = torch_to_mlx(tensor)

Type guard

def mlx_compatible(t: torch.Tensor) -> bool:
    return t.dtype != torch.complex128

Prevention

When it happens

Trigger: Passing a torch.complex128 CPU tensor to torch_to_mlx / mlx_call / MlxTensorView construction (CPU branch).

Common situations: FFT-like pipelines defaulting to complex128 on CPU (e.g. torch.fft outputs), scientific code assuming NumPy-style complex128.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/439964447abc2a4c. Report an issue: GitHub.