{"record":{"id":"439964447abc2a4c","repo":"sgl-project/sglang","slug":"mlx-0-32-does-not-support-complex128-convert-the","errorCode":null,"errorMessage":"MLX 0.32 does not support complex128; convert the Torch tensor to complex64 explicitly","messagePattern":"MLX 0\\.32 does not support complex128; convert the Torch tensor to complex64 explicitly","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/utils/tensor_bridge.py","lineNumber":85,"sourceCode":"    *,\n    copy: bool,\n    synchronize: bool = True,\n) -> mx.array:\n    \"\"\"Convert one tensor, optionally borrowing its MPS allocation.\"\"\"\n    mx = _mlx_core()\n    tensor = tensor.detach()\n\n    if tensor.device.type == \"mps\":\n        if synchronize:\n            # Torch and MLX do not share stream state on Metal.\n            torch.mps.synchronize()\n        return mx.asarray(tensor, copy=copy)\n    if tensor.device.type == \"cpu\":\n        # CPU tensors always get MLX-owned storage.  In particular, do not\n        # expose a NumPy/memoryview alias whose lifetime is controlled by the\n        # caller.\n        if tensor.dtype == torch.complex128:\n            raise ValueError(\n                \"MLX 0.32 does not support complex128; convert the Torch tensor \"\n                \"to complex64 explicitly\"\n            )\n        # MLX 0.32 does not support float64 on its default Metal stream.  Keep\n        # the dtype by constructing this uncommon CPU value on the CPU stream\n        # instead of silently downcasting it to float32.\n        if tensor.dtype == torch.float64:\n            with mx.stream(mx.cpu):\n                return mx.array(tensor, dtype=mx.float64)\n        return mx.array(tensor)\n    raise ValueError(\n        f\"The MLX tensor bridge supports CPU and MPS tensors, got {tensor.device}\"\n    )\n\n\nclass MlxTensorView:\n    \"\"\"A lifetime-bound, zero-copy MLX view of a Torch MPS tensor.\n","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/utils/tensor_bridge.py#L67-L103","documentation":"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.","triggerScenarios":"Passing a torch.complex128 CPU tensor to torch_to_mlx / mlx_call / MlxTensorView construction (CPU branch).","commonSituations":"FFT-like pipelines defaulting to complex128 on CPU (e.g. torch.fft outputs), scientific code assuming NumPy-style complex128.","solutions":["Cast before bridging: t = t.to(torch.complex64), then torch_to_mlx(t)","Use complex64 throughout the pipeline to avoid repeated casts"],"exampleFix":"# before\nmx_t = torch_to_mlx(t)  # t is complex128 -> ValueError\n# after\nmx_t = torch_to_mlx(t.to(torch.complex64))","handlingStrategy":"type-guard","validationCode":"if tensor.dtype == torch.complex128:\n    tensor = tensor.to(torch.complex64)\nmx_t = torch_to_mlx(tensor)","typeGuard":"def mlx_compatible(t: torch.Tensor) -> bool:\n    return t.dtype != torch.complex128","tryCatchPattern":null,"preventionTips":["Standardize on complex64 in mixed torch/MLX pipelines","Check dtype before crossing the bridge"],"tags":["sglang","mlx","dtype","complex-numbers","torch"],"backgroundTag":"unsupported-dtype-conversion","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}