Skip to content

AttributeError: 'Linear' object has no attribute 'weight_scale' — ModelMergeSimple on a quantized (fp8/QuantizedTensor) model: unguarded getattr in get_key_weight vs synthetic quant state-dict keys #14382

@j2gg0s

Description

@j2gg0s

Custom Node Testing

Core nodes only — no custom nodes involved in the repro.

Expected Behavior

ModelMergeSimple (and the other get_key_patches-based merge nodes) should be able to merge a checkpoint whose weights were loaded as QuantizedTensor (e.g. an FP8 e4m3fn mixed-precision quantized checkpoint). The real .weight keys already have a working convert_weight dequantization path in get_key_weight, so quantized merging is clearly intended to work.

Actual Behavior

Deterministic crash at the merge node, before any sampling, whenever model2 (the model get_key_patches is called on) is a quantized checkpoint:

AttributeError: 'Linear' object has no attribute 'weight_scale'

Observed on a v0.24.0-based build; I verified the relevant code is unchanged on current master (line numbers below are from master).

Steps to Reproduce

  1. Load an FP8 e4m3fn quantized checkpoint (e.g. a quantized FLUX.2 Klein — any checkpoint that carries comfy_quant metadata so its weights load as QuantizedTensor via the Mixed Precision Quantization System, Mixed Precision Quantization System #10498 / Make old scaled fp8 format use the new mixed quant ops system. #11000).
  2. Load any second model of the same architecture.
  3. Connect both to ModelMergeSimple (quantized model as model2) and queue.
  4. Instant AttributeError — 100% reproducible, no GPU/sampling needed.

Affects every node that calls get_key_patches on a quantized model: ModelMergeSimple, ModelMergeBlocks, ModelMergeSubtract, ModelMergeAdd, CLIPMergeSimple, CheckpointSave-after-merge, etc.

Debug Logs

Traceback (most recent call last):
  File "ComfyUI/comfy_extras/nodes_model_merging.py", line 28, in merge
    kp = model2.get_key_patches("diffusion_model.")
  File "ComfyUI/comfy/model_patcher.py", line 822, in get_key_patches
    weight, set_func, convert_func = get_key_weight(self.model, k)
  File "ComfyUI/comfy/model_patcher.py", line 186, in get_key_weight
    weight = getattr(op, op_keys[1])
  File "torch/nn/modules/module.py", line 1928, in __getattr__
    raise AttributeError(
AttributeError: 'Linear' object has no attribute 'weight_scale'

(Traceback trimmed to the relevant frames; from a headless server deployment.)

Other

Root cause

The Mixed Precision Quantization System makes quantized layers' state_dict() emit synthetic top-level keys that are not module attributes:

  • comfy/ops.py:1118 _quantized_weight_state_dict writes {prefix}weight_scale (via module.weight.state_dict(f"{prefix}weight")), {prefix}comfy_quant (line 1136), {prefix}input_scale (extra_quant_params, line 1181), and {prefix}weight_scale_2 for nvfp4.
  • On load these keys are popped and absorbed into the QuantizedTensor layout params — _load_quantized_module explicitly skips register_parameter for weight_scale / weight_scale_2 (comfy/ops.py:1103-1104). So they exist only in the state dict, never on the module.

Meanwhile ModelPatcher.get_key_patches (comfy/model_patcher.py:813) iterates all model_state_dict() keys and feeds each one to get_key_weight, which does an unguarded lookup (comfy/model_patcher.py:186):

weight = getattr(op, op_keys[1])

For some.layer.weight_scale this resolves the module fine but then hits torch.nn.Module.__getattr__AttributeError.

The old fix f9f9faf ("Fixed model merging issue with scaled fp8", 2024) only covered the legacy scaled_fp8 buffer format, which is a real module attribute — it doesn't help with the new synthetic keys.

Suggested fix (running in our fork, resolves the crash; quantized .weight keys keep their convert_weight dequant path so merging still works as designed):

# comfy/model_patcher.py get_key_weight
weight = getattr(op, op_keys[1], None)
if weight is None:
    return None, None, None

plus skipping None entries in get_key_patches:

weight, set_func, convert_func = get_key_weight(self.model, k)
if weight is None:
    continue

If returning None from get_key_weight is considered too broad (see the discussion in #11585 about not sweeping bugs under the rug), an alternative is for get_key_patches to explicitly skip the known synthetic suffixes (comfy_quant, weight_scale, weight_scale_2, input_scale). Unlike #11585 (triggered by a custom node's RMS_norm.gamma), here the offending keys are generated by core's own quantized state_dict(), so core nodes alone hit it.

Related but not duplicates

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions