{"record":{"id":"2e1d37be170dc741","repo":"sgl-project/sglang","slug":"mlxtensorview-requires-a-torch-mps-tensor-got-ow","errorCode":null,"errorMessage":"MlxTensorView requires a Torch MPS tensor, got {owner.device}","messagePattern":"MlxTensorView requires a Torch MPS tensor, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/utils/tensor_bridge.py","lineNumber":118,"sourceCode":"\nclass MlxTensorView:\n    \"\"\"A lifetime-bound, zero-copy MLX view of a Torch MPS tensor.\n\n    The view deliberately retains a detached Torch tensor *and* the imported\n    MLX array.  Holding only the array is insufficient: a later parameter\n    replacement or garbage collection could invalidate the borrowed storage\n    while MLX still has a lazy graph referring to it. This class is intended\n    for immutable inference weights; construct a new view after replacing the\n    source storage.\n    \"\"\"\n\n    __slots__ = (\"torch_tensor\", \"array\")\n\n    def __init__(self, tensor: torch.Tensor, *, synchronize: bool = True):\n        with _BRIDGE_LOCK:\n            owner = tensor.detach()\n            if owner.device.type != \"mps\":\n                raise ValueError(\n                    f\"MlxTensorView requires a Torch MPS tensor, got {owner.device}\"\n                )\n            if synchronize:\n                torch.mps.synchronize()\n            self.torch_tensor = owner\n            self.array = _torch_to_mlx(owner, copy=False, synchronize=False)\n\n    @classmethod\n    def _from_synchronized(cls, tensor: torch.Tensor) -> MlxTensorView:\n        view = object.__new__(cls)\n        owner = tensor.detach()\n        if owner.device.type != \"mps\":\n            raise ValueError(\n                f\"MlxTensorView requires a Torch MPS tensor, got {owner.device}\"\n            )\n        view.torch_tensor = owner\n        view.array = _torch_to_mlx(owner, copy=False, synchronize=False)\n        return view","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/utils/tensor_bridge.py#L100-L136","documentation":"MlxTensorView is a lifetime-bound zero-copy view and only exists for MPS-backed torch tensors. Its constructor detaches the tensor and requires owner.device.type == 'mps', otherwise raising with the offending device.","triggerScenarios":"Constructing MlxTensorView(cpu_tensor) or MlxTensorView(cuda_tensor) directly.","commonSituations":"Unit tests or helper code constructing views on CPU fixtures; forgetting .to('mps') before wrapping.","solutions":["Move the tensor to MPS first: MlxTensorView(t.to('mps'))","Use torch_to_mlx for CPU tensors (it copies) instead of a view"],"exampleFix":"# before\nview = MlxTensorView(t)  # t on cpu\n# after\nview = MlxTensorView(t.to('mps'))","handlingStrategy":"type-guard","validationCode":"assert tensor.device.type == \"mps\", \"MlxTensorView needs an MPS tensor\"","typeGuard":"def is_mps(t: torch.Tensor) -> bool:\n    return t.device.type == \"mps\"","tryCatchPattern":null,"preventionTips":["Use the public borrow_torch_tensors/torch_to_mlx instead of constructing views directly"],"tags":["sglang","mlx","mps","device","torch"],"backgroundTag":"unsupported-device","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}