Files
ussoewwin-ComfyUI-QwenImage…/md/PR28_FIX_EXPLANATION.md
T

9.9 KiB

Complete Explanation of PR #28 and Additional Fixes


① Proposal Content and What the Problem Was

PR #28 Proposal Content

Title: Fix missing 1 required positional argument: 'cpu_offload'

Problem Description: After the kjai node update, the following error occurred:

TypeError: load_lora() missing 1 required positional argument: 'cpu_offload'

or

TypeError: load_lora_stack() missing 1 required positional argument: 'cpu_offload'

Root Cause

Code Before Fix:

def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str):
    # cpu_offload is a required argument (no default value)

Problems:

  1. Required Parameter: cpu_offload was a required parameter without a default value, causing errors when not provided
  2. ComfyUI Parameter Passing: ComfyUI automatically passes required parameters from INPUT_TYPES() to methods, but kjai node updates changed how parameters are passed in some call paths
  3. Compatibility Issues: In certain node invocation scenarios (old workflows, dynamic node generation, or kjai node updates), the parameter could be missing

Why It Became a Problem:

  • Even though cpu_offload is defined as required in INPUT_TYPES(), ComfyUI's node invocation mechanism doesn't always guarantee that all parameters are passed
  • The kjai node update modified how nodes are invoked, potentially causing parameter omissions in some call paths
  • This is a compatibility issue that arose from external node updates

② How We Fixed It (File Names and Code)

Files Modified

  1. nodes/lora/qwenimage.py - 4 locations fixed
  2. nodes/lora/qwenimage_v2.py - 2 locations fixed

Fix 1: PR #28 Fixes (FUNCTION Methods)

File: nodes/lora/qwenimage.py

Fix Location 1: NunchakuQwenImageLoraLoader.load_lora() method (line 91)

# Before
def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str):

# After
def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str = "disable"):

Fix Location 2: NunchakuQwenImageLoraStack.load_lora_stack() method (line 271)

# Before
def load_lora_stack(self, model, lora_count, cpu_offload, **kwargs):

# After
def load_lora_stack(self, model, lora_count, cpu_offload="disable", **kwargs):

File: nodes/lora/qwenimage_v2.py

Fix Location 3: NunchakuQwenImageLoraStackV2.load_lora_stack() method (line 113)

# Before
def load_lora_stack(self, model, lora_count, cpu_offload, **kwargs):

# After
def load_lora_stack(self, model, lora_count, cpu_offload="disable", **kwargs):

Fix 2: Additional Fixes Applied (IS_CHANGED Methods)

File: nodes/lora/qwenimage.py

Fix Location 4: NunchakuQwenImageLoraLoader.IS_CHANGED() method (line 36)

# Before
@classmethod
def IS_CHANGED(s, model, lora_name, lora_strength, cpu_offload, *args, **kwargs):
    """
    Detect changes to trigger node re-execution.
    Returns a hash of relevant parameters to detect changes.
    """
    import hashlib
    m = hashlib.sha256()
    m.update(lora_name.encode())
    m.update(str(lora_strength).encode())
    m.update(str(model).encode())
    m.update(cpu_offload.encode())
    return m.digest().hex()

# After
@classmethod
def IS_CHANGED(s, model, lora_name, lora_strength, cpu_offload="disable", *args, **kwargs):
    """
    Detect changes to trigger node re-execution.
    Returns a hash of relevant parameters to detect changes.
    """
    import hashlib
    m = hashlib.sha256()
    m.update(lora_name.encode())
    m.update(str(lora_strength).encode())
    m.update(str(model).encode())
    m.update(cpu_offload.encode())
    return m.digest().hex()

Fix Location 5: NunchakuQwenImageLoraStack.IS_CHANGED() method (line 195)

# Before
@classmethod
def IS_CHANGED(cls, model, lora_count, cpu_offload, **kwargs):
    """
    Detect changes to trigger node re-execution.
    Returns a hash of relevant parameters to detect changes.
    """
    import hashlib
    m = hashlib.sha256()
    m.update(str(model).encode())
    m.update(str(lora_count).encode())
    m.update(cpu_offload.encode())
    # Hash all LoRA parameters
    for i in range(1, 11):
        m.update(kwargs.get(f"lora_name_{i}", "").encode())
        m.update(str(kwargs.get(f"lora_strength_{i}", 0)).encode())
    return m.digest().hex()

# After
@classmethod
def IS_CHANGED(cls, model, lora_count, cpu_offload="disable", **kwargs):
    """
    Detect changes to trigger node re-execution.
    Returns a hash of relevant parameters to detect changes.
    """
    import hashlib
    m = hashlib.sha256()
    m.update(str(model).encode())
    m.update(str(lora_count).encode())
    m.update(cpu_offload.encode())
    # Hash all LoRA parameters
    for i in range(1, 11):
        m.update(kwargs.get(f"lora_name_{i}", "").encode())
        m.update(str(kwargs.get(f"lora_strength_{i}", 0)).encode())
    return m.digest().hex()

File: nodes/lora/qwenimage_v2.py

Fix Location 6: NunchakuQwenImageLoraStackV2.IS_CHANGED() method (line 36)

# Before
@classmethod
def IS_CHANGED(cls, model, lora_count, cpu_offload, **kwargs):
    """
    Detect changes to trigger node re-execution.
    Returns a hash of relevant parameters to detect changes.
    """
    import hashlib
    m = hashlib.sha256()
    m.update(str(model).encode())
    m.update(str(lora_count).encode())
    m.update(cpu_offload.encode())
    # Hash all LoRA parameters
    for i in range(1, 11):
        m.update(kwargs.get(f"lora_name_{i}", "").encode())
        m.update(str(kwargs.get(f"lora_strength_{i}", 0)).encode())
    return m.digest().hex()

# After
@classmethod
def IS_CHANGED(cls, model, lora_count, cpu_offload="disable", **kwargs):
    """
    Detect changes to trigger node re-execution.
    Returns a hash of relevant parameters to detect changes.
    """
    import hashlib
    m = hashlib.sha256()
    m.update(str(model).encode())
    m.update(str(lora_count).encode())
    m.update(cpu_offload.encode())
    # Hash all LoRA parameters
    for i in range(1, 11):
        m.update(kwargs.get(f"lora_name_{i}", "").encode())
        m.update(str(kwargs.get(f"lora_strength_{i}", 0)).encode())
    return m.digest().hex()

Summary of Fixes

File Class Method Line Fix Type
qwenimage.py NunchakuQwenImageLoraLoader load_lora() 91 PR #28
qwenimage.py NunchakuQwenImageLoraLoader IS_CHANGED() 36 Additional
qwenimage.py NunchakuQwenImageLoraStack load_lora_stack() 271 PR #28
qwenimage.py NunchakuQwenImageLoraStack IS_CHANGED() 195 Additional
qwenimage_v2.py NunchakuQwenImageLoraStackV2 load_lora_stack() 113 PR #28
qwenimage_v2.py NunchakuQwenImageLoraStackV2 IS_CHANGED() 36 Additional

Total: 2 files, 6 locations fixed


③ What It Means

1. Technical Meaning

Effect of Default Arguments:

# Before (required argument)
def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str):
    # Error if cpu_offload is not provided

# After (with default value)
def load_lora(self, model, lora_name: str, lora_strength: float, cpu_offload: str = "disable"):
    # Uses "disable" if cpu_offload is not provided

Behavior:

  • If cpu_offload is passed, that value is used
  • If cpu_offload is missing, the default value "disable" is automatically used
  • This prevents TypeError and ensures the function always receives a valid value

2. Why "disable"?

Looking at the INPUT_TYPES() definition:

"cpu_offload": (
    ["auto", "enable", "disable"],
    {
        "default": "disable",
        "tooltip": "CPU offload setting. 'auto' enables offload when VRAM is low, 'enable' forces offload, 'disable' disables offload.",
    },
),

Reason:

  • The UI default value is "disable"
  • To maintain consistency between UI defaults and implementation defaults
  • "disable" means "no CPU offload" (better performance when VRAM is sufficient), which is the recommended default

3. Functional Meaning

Fix for FUNCTION Methods:

  • Prevents errors when cpu_offload parameter is missing in load_lora() and load_lora_stack()
  • Ensures compatibility with kjai node updates

Fix for IS_CHANGED Methods:

  • ComfyUI calls IS_CHANGED() to detect changes and determine if node re-execution is needed
  • If cpu_offload is missing in IS_CHANGED() calls, it could cause AttributeError when calling .encode()
  • Adding default values prevents errors in change detection as well

4. Compatibility Meaning

Backward Compatibility:

  • Existing workflows continue to work without modification
  • Maintains compatibility with updated or new nodes

Future-Proofing:

  • Prevents similar errors when parameters are missing in various invocation scenarios
  • More robust integration with external nodes

5. Design Meaning

Defensive Programming:

  • Protects against unexpected external factors (like node updates)
  • Provides fallback behavior through default values

Consistency:

  • UI defaults match implementation defaults
  • User expectations align with actual behavior

6. Practical Meaning

Error Prevention:

  • Eliminates TypeError: missing 1 required positional argument errors
  • More robust operation

User Experience:

  • Fewer errors for end users
  • More stable operation

Summary

Problem

After the kjai node update, cpu_offload parameter could be missing, causing TypeError.

Solution

Added default value "disable" to both FUNCTION methods (load_lora, load_lora_stack) and IS_CHANGED methods for all three node classes.

Result

  • ✅ Error prevention
  • ✅ Backward compatibility maintained
  • ✅ Consistency with UI defaults
  • ✅ More robust implementation

This fix is a defensive programming approach to handle compatibility issues caused by external node updates.