Why you get 'expected all tensors to be on the same device', and where the mismatched tensor actually comes from
RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cuda:1!
Also appears as
- RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!
- RuntimeError: Input type (torch.cuda.FloatTensor) and weight type (torch.FloatTensor) should be the same
Short answer
This error means a tensor was created or moved to a specific device, often cuda:0 or cpu, that does not match the device another tensor in the same operation lives on, which happens most often when leftover manual .to(device) calls from single-GPU code collide with automatic sharding from device_map='auto' or DistributedDataParallel. The fix is to trace exactly which tensor has the wrong device with a quick print of tensor.device, not to guess and add more .to() calls.
Affects: PyTorch and HuggingFace transformers models using device_map='auto', DataParallel, or manual multi-GPU placement, most common when migrating single-GPU code to multi-GPU
Find the specific hardcoded device call before adding more .to() calls
- 1Search your codebase for every literal .to("cuda") or .cuda() call and check whether any of them run after the model was loaded with device_map='auto'.
- 2If using device_map='auto', print model.hf_device_map to see the actual per-layer placement, then make sure inputs are moved to the device of the model's first layer, not a hardcoded cuda:0.
- 3Check any custom data collator or preprocessing function for tensors it creates directly (labels, position_ids, attention_mask), since these often default to CPU or a hardcoded device instead of matching the batch.
- 4If using Trainer or accelerate, let the framework handle device placement entirely and remove any manual .to(device) calls on the model or optimizer that predate the multi-GPU setup.
- 5Add a one-line assertion right before the failing operation during debugging to pinpoint exactly which tensor is wrong, then remove the assertion once fixed.
How to confirm this is your problem
- Error appears only after moving from single-GPU to multi-GPU or device_map='auto' setups, never on a single device
- The error names two specific devices, often cuda:0 and another GPU, or cuda:0 and cpu
- Custom training loops or custom collators are involved; the error rarely occurs with unmodified library training loops
- The failure happens consistently on the same operation, such as loss computation or a position embedding lookup, every run
Root causes and fixes
A manual .to(device) call left over from earlier single-GPU code now conflicts with a model sharded automatically across multiple devices
When code is first written for a single GPU, it is common to hardcode .to("cuda") or .cuda() on the model, inputs, or specific tensors. Once the model is later loaded with device_map='auto', which places different layers on different GPUs, that hardcoded call pins one tensor to a fixed device while the model expects inputs to match whatever device each layer actually landed on, causing a mismatch the moment that layer runs.
Fix: Remove hardcoded .to("cuda") calls on the model itself when using device_map='auto', and move inputs to the device of the model's embedding layer rather than a fixed index.
A custom data collator or preprocessing step creates new tensors (labels, position_ids, custom masks) that default to CPU or a different device
Collators often build auxiliary tensors from scratch, which default to CPU unless a device is explicitly specified. If the rest of the batch has already been moved to GPU by the training loop but the collator's output has not, the mismatch surfaces the first time the two tensors interact, such as in a loss or attention computation.
Fix: Either leave all collator output on CPU and let the Trainer or accelerate move the whole batch to the correct device afterward, or explicitly set the device to match the batch when the collator creates new tensors.
device_map='auto' automatic sharding is combined with an explicit .to("cuda:0") call on the model or specific inputs
device_map='auto' uses the accelerate library to compute a per-layer device assignment based on available VRAM across GPUs, and expects nothing else to override that placement afterward. A subsequent .to("cuda:0") on the whole model attempts to move every parameter to a single device, which can partially succeed or silently break the mapping accelerate had already set up, leaving some tensors on their originally assigned device and others moved.
Fix: Never call .to(device) on a model loaded with device_map='auto'; if you need to move only the inputs, query the model's actual device placement instead of assuming cuda:0.
A cached buffer (rotary embedding cache, KV cache, precomputed positional encoding) is created once at init time on whatever device was active then
Some custom model implementations precompute and cache tensors like rotary embedding tables the first time they are needed, using whatever device is current at that moment. If that first call happens before multi-GPU placement is finalized, or from a different rank than later calls, the cached buffer stays pinned to the wrong device for subsequent calls.
Fix: Register such buffers with register_buffer() so they move automatically with the module's .to() calls, or explicitly move the cache to match the input tensor's device at each call rather than caching a device assumption.
A custom stopping criteria, logits processor, or generation utility allocates a new tensor without specifying a device
Custom generation utilities sometimes create small tensors, such as a done flag or an EOS check tensor, which default to CPU, then combine them with GPU logits tensors during the generation loop, producing the same device mismatch pattern in a code path that is easy to overlook since it is not part of the main forward pass.
Fix: Explicitly pass a matching device to every tensor-creation call inside custom generation logic, matching whatever tensor it will be combined with.
Diagnostic commands
Print actual per-layer device placement for device_map='auto' models
python -c "print(model.hf_device_map)"
Shows exactly which layer lives on which device; use this to confirm where inputs need to be moved instead of assuming cuda:0 for everything.
Print the device of every tensor going into the failing operation
python -c "print({k: v.device for k, v in batch.items()})"Any key showing a device different from the rest of the batch, or from the model's first layer, is the specific tensor causing the mismatch and should be traced back to where it was created.
Search the codebase for hardcoded device calls
grep -rn 'cuda(' --include='*.py' .Every hit is a candidate for the leftover single-GPU code that conflicts with automatic multi-GPU placement; review each one against whether the model uses device_map='auto' or DDP.
Stopping it from happening again
- Avoid hardcoding device strings anywhere in model or data pipeline code; always derive the device from an existing tensor or the model's own placement.
- Register any custom cached buffers with register_buffer() so PyTorch's built-in device-movement machinery handles them automatically.
- Code-review multi-GPU pull requests specifically for leftover .to("cuda") or .cuda() calls inherited from single-GPU development.
- Add an integration test that runs the training or inference path on at least two GPUs in CI, since this class of bug is invisible on single-GPU test runs.
When this becomes an architecture problem
If device placement bugs keep recurring across a growing custom codebase built around device_map='auto', it is worth standardizing on a single, well-tested placement pattern, or a maintained framework like accelerate or DeepSpeed, rather than continuing to hand-roll device management, since the failure mode scales with code complexity, not GPU count.
Frequently asked questions
Why does this error only show up with multiple GPUs, never with one?
On a single GPU, every tensor moved to 'cuda' lands on the same physical device, so there is nothing to mismatch. Multi-GPU setups introduce more than one valid device target, which is what exposes any code that assumed a single fixed device.
Should I add .to(model.device) everywhere to fix this?
Only if the model actually lives on a single device. If you are using device_map='auto', the model itself spans multiple devices and has no single device, so blindly calling .to(model.device) will not work and can mask the real issue; use model.hf_device_map or the first layer's device instead.
Does DataParallel have this problem too, or only device_map='auto'?
DataParallel replicates the whole model onto each GPU and handles device placement internally for its own forward pass, so it is less prone to this specific error than device_map='auto' sharding, but custom code that creates tensors outside the managed forward call can still trigger the same mismatch.
Size it properly next time
Free calculators that prevent this class of failure before you provision hardware.
Multi-GPU Tensor Parallelism Calculator
Model how tensor-parallel throughput actually scales across multiple GPUs, accounting for interconnect overhead that keeps scaling sub-linear.
Free ToolGPU Sizing Calculator for LLM Inference
Work out how many GPUs you need to serve a given open-weight model to your user base, based on memory footprint and token throughput.
Free ToolKV Cache Memory Calculator
Calculate KV cache memory per sequence and per batch from model architecture and context length, then see how many concurrent sequences your GPU can hold.
Related problems
Tensor parallelism fails because the model does not split evenly across GPUs
Tensor parallelism fails when the chosen degree does not evenly divide the model's attention heads, and often its key/value heads and hidden size, so the framework cannot split the projection weights across ranks. It also fails in practice, without an assertion, when the GPUs assigned to the TP group differ in VRAM or compute, since the even weight split then fits some ranks and not others.
HuggingFace Accelerate config mismatch causes wrong distributed launch
Accelerate errors during launch almost always mean the saved configuration does not match the current machine's actual GPU count, node count, or distributed type, or that the model was loaded with device_map='auto' inference-style sharding and then also handed to accelerate's training-mode preparation, which are two incompatible placement strategies. Regenerating the config for the current machine, or passing explicit CLI overrides, resolves most cases.
GPU memory stays full after inference finishes
This is expected PyTorch behavior, not a leak: the caching allocator keeps freed GPU memory reserved for future allocations instead of returning it to the driver, so nvidia-smi shows the process's total reserved memory rather than what is actually in use. The real leak to check for is a growing number across requests (Python references keeping tensors alive), not a single high plateau after one inference call.
CUDA out of memory when loading an LLM
This happens because model weights alone require roughly 2 bytes per parameter in fp16/bf16 (a 70B model needs about 140 GB before you even run inference), and that number does not fit your GPU. The fix is to either quantize the weights (AWQ, GPTQ, FP8, or GGUF), split the model across multiple GPUs with tensor parallelism, or pick a GPU with enough VRAM for the parameter count you are loading.
GuideMulti-GPU LLM Serving: Tensor vs Pipeline Parallelism
Multi-GPU LLM serving explained: tensor parallelism vs pipeline parallelism, NCCL interconnect requirements, and when to split a model across GPUs.
GuidevLLM Production Deployment: A Practitioner's Guide
Deploy vLLM in production: continuous batching, PagedAttention, config flags that matter, and the metrics to watch before you trust it with real traffic.
GuideDeploying Llama Models On-Prem for Enterprise
How to deploy Llama models on-prem for enterprise use: hardware sizing, quantization, vLLM serving, licensing, and security for regulated manufacturers.
Still stuck, or tired of fighting your own infrastructure?
Netray deploys and operates on-prem AI for regulated manufacturers and defense suppliers. We have debugged this stack in production, on air-gapped networks, at scale.