Skip to content

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.

Quick answer

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.

Environment#vllm#nvfp4#quantization#emulation#device-mismatch#modelopt

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

SymptomWhy 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_bytesThe 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 backendsIt 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 generationPyTorch 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)

training.log (synthetic 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 analysis

No 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 reference pattern
// 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 / StackRecommendationNotes
Batch-invariant mode setClear it and re-check the selected backendIt is the reported cause of the accelerated backends being excluded.
No NVFP4 backend on the deviceServe a format the hardware supportsEmulation is a correctness fallback, not a serving path.
Any NVFP4 deploymentAssert the selected backend at startupTurns a silent fallback into a refusal to start.
Works on one machine, not anotherCompare the backend selection lines, not the checkpointsAvailability is a property of the hardware and build pair.

With the fix vs without the fix

DimensionWith the fixWithout the fix
What the error namesA PyTorch indexing device ruleRead as a problem with the checkpoint
Why one machine worksIt selected an accelerated backendAssumed to be a difference in the weights
Role of emulationA correctness fallback rarely exercisedAssumed 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

Where the two tensors live
  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 EMULATION
The constant unpacking table is allocated on the CPU while the indices addressing it have already moved to the accelerator. PyTorch requires both on one device, so the emulated unpack fails the first time it runs.

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?
No. The same weights serve correctly wherever an accelerated NVFP4 backend is available. The failure is in the software fallback that unpacks them.
Can I just move the lookup table to the GPU?
That would address the symptom, but emulation is a correctness reference rather than a serving path. Restoring an accelerated backend is both the faster and the intended outcome.
Why did it pick emulation at all?
Backend selection is automatic and silent. Something excluded the accelerated options, and batch-invariant mode is the reported cause.
Why does it crash before generating anything?
The unpacking happens while weights are prepared, so the failure lands during engine initialisation rather than on a request.

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.