[Bugfix] Detect unloaded NVFP4 weight scales with a NaN sentinel (#52501)
Signed-off-by: pavelzak <pavel.zakharov@gmail.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: OpenAI Codex <codex@openai.com>
Pavel Zakharov committed
Sep 13, 2026 at 07:58 UTC
b7e0cdac5d11e3a1b32745079cf5591a685aa721
1 file changed
+18
-1
vllm/model_executor/layers/quantization/modelopt.py
+18
-1
@@ -1942,7 +1942,9 @@ class KNvfp4Static(QuantKeyScheme):
1942
PerTensorScaleParameter,
1943
wl,
1944
)
1945
- # Per-block (group_size) weight scale.
1945
+ # Per-block (group_size) weight scale. Initialized to NaN (rather than
1946
+ # left as torch.empty garbage) so an unloaded scale is unambiguously
1947
+ # detectable in process() below, matching the sentinel pattern in #45320.
1948
self.register_params(
1949
layer,
1950
"weight_scale",
@@ -1955,11 +1957,26 @@ class KNvfp4Static(QuantKeyScheme):
1957
wl,
1958
input_dim=1,
1959
output_dim=0,
1960
+ init=float("nan"),
1961
)
1962
1963
def process(self, layer, role) -> None:
1964
if role is not WEIGHT:
1965
self.reject(role)
1966
+ # Sanity-check: weight_scale must have been overwritten by the weight
1967
+ # loader. A value still equal to the NaN sentinel it was created with
1968
+ # means the FP4 weights were never actually loaded (e.g. the
1969
+ # checkpoint stores this layer as BF16 and the weight loader silently
1970
+ # skipped it).
1971
+ if torch.isnan(layer.weight_scale.float()).any():
1972
+ raise RuntimeError(
1973
+ f"NVFP4 weight_scale for layer "
1974
+ f"{getattr(layer, 'name', repr(layer))!r} was never loaded "
1975
+ "(still NaN). The checkpoint likely stores this layer as "
1976
+ "BF16 (not FP4). Fix: pass quant_config=None when "
1977
+ "constructing this layer, or add it to the quantization "
1978
+ "ignore list."
1979
+ )
1980
if torch.unique(layer.weight_scale_2).numel() != 1:
1981
logger.warning_once(
1982
"In NVFP4 linear, the global weight scale differs across "