vLLM crashes initialising an NVFP4 model because the emulation lookup table stays on the CPU
When no hardware NVFP4 backend is available, vLLM falls back to an emulation path that unpacks four-bit values through a small constant lookup table. The table is indexed by a tensor that lives on the GPU while the table itself was built on the CPU, so engine initialisation fails before a single token is generated.
Read the startup line naming the selected NvFp4 backend. If it says EMULATION, the accelerated backends were excluded — usually by batch-invariant mode — and restoring one avoids the broken fallback entirely.
What this failure is
An initialisation-time crash in vLLM's software NVFP4 fallback, where a constant unpacking table allocated on the CPU is indexed by accelerator-resident values, violating PyTorch's requirement that both live on one device.
Why it happens (the mechanism)
Backend selection is automatic and silent, and emulation is the last resort. Because it is rarely reached, its helper carries an assumption that no accelerated path makes: that the small constant table and the values indexing it are on the same device. The moment the fallback is genuinely used, that assumption meets a tensor already moved to the accelerator and PyTorch refuses the operation.
What you'll observe
- The model loads and then the engine dies during initialisation rather than during generation
- The same checkpoint is reported working elsewhere, on hardware with a real NVFP4 backend
- The error names an indexing rule rather than the quantization format, so the connection is not obvious
- Nothing in the launch command mentions emulation, which was selected automatically
Common symptoms and what they mean
| Symptom | Why it happens |
|---|---|
| RuntimeError: indices should be either on cpu or on the same device as the indexed tensor (cpu) | PyTorch requires the index tensor and the tensor being indexed to live on one device, or the index to be on the CPU. The emulation helper builds its constant table as a plain tensor, which lands on the CPU, and then indexes it with values that have already been moved to the accelerator, so the pair straddles the boundary the rule forbids. |
| Traceback ending in nvfp4_emulation_utils.py, in break_fp4_bytes | The path is only reached when backend selection has exhausted every accelerated option. That makes the failure look hardware-specific when it is really a fallback that few deployments exercise, which is why the same checkpoint is reported working on machines that never enter emulation. |
| A log line stating that the EMULATION NvFp4 MoE backend was selected out of the potential backends | It fires during initialisation because the unpacking happens while weights are being prepared rather than while tokens are being produced, so there is no partially working server to inspect. |
| The crash occurring during engine init, before any generation | PyTorch requires the index tensor and the tensor being indexed to live on one device, or the index to be on the CPU. The emulation helper builds its constant table as a plain tensor, which lands on the CPU, and then indexes it with values that have already been moved to the accelerator, so the pair straddles the boundary the rule forbids. |
Which systems are affected
- NVFP4 and modelopt_fp4 checkpoints served by vLLM
- Hardware or builds where no FlashInfer, CUTLASS or Marlin NVFP4 backend is available, leaving emulation as the only option
- Mixture-of-experts models, where the emulated path is reached through the MoE layers
- Deployments that set the batch-invariant mode, which can exclude the faster backends
How to confirm this is the problem
Apply the following checklist to a small reproduction: each box below is a positive signal that you are looking at this exact failure rather than a sibling in the same taxonomy.
- ✓Search the startup log for the line naming the selected NvFp4 backend. Emulation there confirms the fallback was taken and the accelerated paths were unavailable.
- ✓Check whether batch-invariant mode is set in the environment, since it can remove the accelerated backends from consideration.
- ✓Launch the identical checkpoint on hardware with a supported NVFP4 backend. Success there localises the fault to the fallback path rather than to the weights.
Example training logs (fingerprint)
[nvfp4.py:283] Using 'EMULATION' NvFp4 MoE backend out of potential backends: ['FLASHINFER_TRTLLM', 'FLASHINFER_CUTEDSL', 'FLASHINFER_CUTLASS', 'VLLM_CUTLASS', 'MARLIN', 'EMULATION'].
File ".../vllm/model_executor/layers/quantization/utils/nvfp4_emulation_utils.py", line 38, in break_fp4_bytes
values = kE2M1[abs_vals] * torch.where(signs, -1.0, 1.0)
RuntimeError: indices should be either on cpu or on the same device as the indexed tensor (cpu)Timestamps and exact values vary across runs, but the pattern. An info-level start, an early WARN, an ERROR carrying the symptom. Is the actual fingerprint you should alert on. The Denpex platform flags this combination automatically.
Root cause, fix & prevention, signed in
Sign up free to see why this failure really happens, the exact remediation steps, and the production-grade prevention pattern. You also get 3 free full diagnoses for your own training logs.
Sign up free. Unlock the full analysisNo credit card · 3 free diagnoses · Instant access
Why the recommended fix works
Restoring an accelerated backend removes the emulated helper from the execution path altogether, so the mismatched pair is never constructed. It is a better fix than forcing the table onto the accelerator would be, because emulation is a correctness reference rather than something a served deployment should be running on at all.
Code examples
// Typical pattern:
import torch.optim as optim
optimizer = optim.AdamW(model.parameters(), lr=3e-4)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=num_training_steps)
for step in range(num_training_steps):
optimizer.zero_grad()
loss = model(batch)
loss.backward()
optimizer.step()
scheduler.step()Adapt the snippet to your framework. The same pattern holds for PyTorch Lightning, Hugging Face Trainer, DeepSpeed, Megatron-LM, and vLLM training wrappers. Where the wrapper exposes a config flag (for examplelr_scheduler_type in Trainer), prefer the flag over the imperative API to keep the schedule declarative and reproducible.
Best practices by model family
| Model / Stack | Recommendation | Notes |
|---|---|---|
| Batch-invariant mode set | Clear it and re-check the selected backend | It is the reported cause of the accelerated backends being excluded. |
| No NVFP4 backend on the device | Serve a format the hardware supports | Emulation is a correctness fallback, not a serving path. |
| Any NVFP4 deployment | Assert the selected backend at startup | Turns a silent fallback into a refusal to start. |
| Works on one machine, not another | Compare the backend selection lines, not the checkpoints | Availability is a property of the hardware and build pair. |
With the fix vs without the fix
| Dimension | With the fix | Without the fix |
|---|---|---|
| What the error names | A PyTorch indexing device rule | Read as a problem with the checkpoint |
| Why one machine works | It selected an accelerated backend | Assumed to be a difference in the weights |
| Role of emulation | A correctness fallback rarely exercised | Assumed to be a supported serving path |
Real engineering notes
“The most useful line in the whole log is the one nobody reads: the backend selection notice printed before the traceback. It names the chosen backend and every alternative that was considered, which turns an opaque indexing error into an obvious statement that the deployment fell back to software. Assert on that line at startup and this class of failure stops being a crash and becomes a configuration message.”
Visual fingerprint
kE2M1 lookup table built as a plain tensor -> CPU
abs_vals indices unpacked from weights -> GPU
|
kE2M1[abs_vals] -> RuntimeError
reached only when backend selection falls through to EMULATIONRelated failures to investigate next
Root cause, fix & prevention
Frequently asked questions
Twelve targeted questions that engineers and on-call staff most commonly ask about this failure.
Is the checkpoint corrupt?
Can I just move the lookup table to the GPU?
Why did it pick emulation at all?
Why does it crash before generating anything?
Don't just read the fix, diagnose your run
The encyclopedia tells you what went wrong. Denpex tells you what went wrong in YOUR training run. With your logs, your config, and your stack.