メインコンテンツへスキップ
見出し画像

Fixed Nunchaku v1.2.1 to run Flux1 PulID

    Ariana Grande - Problem (Radio Disney Music Awards 2014)

    …After all, PulID always becomes the bomb for Nunchaku.

    ComfyUI-nunchaku Fix Details - Complete Explanation

    Document Information

    • Relative Path: Fix_Details_Explanation.md

    • Absolute Path: D:\USERFILES\ComfyUI\Fix_Details_Explanation.md

    Fixed Files List

    画像

    Overview

    This document provides a complete and detailed explanation of five fixes applied to the ComfyUI-nunchaku package.

    Fixes 1–2 were previously applied. Fixes 3–5 were newly applied to address errors that occur when using an unofficial loader (ComfyUI-nunchaku-unofficial-loader) alongside the official nunchaku nodes.


    Fix 1: flux.py - Error Fix for device_id Being None

    Error Content

    RuntimeError: Invalid device string: 'cuda:None'
    
    File "...\nodes\models\flux.py", line 208, in load_model
        device = torch.device(f"cuda:{device_id}")
    RuntimeError: Invalid device string: 'cuda:None'
    

    Error Cause

    • Location: Line 208 of NunchakuFluxDiTLoader.load_model method

    • Root Cause: When the device_id parameter is passed as None, torch.device(f"cuda:{device_id}") generates an invalid device string "cuda:None". PyTorch's torch.device() does not accept this, causing a RuntimeError.

    • Occurrence Conditions:

      • When device_id is not explicitly set in ComfyUI node configuration

      • When the node's default value is not properly applied

      • When a specific GPU is not specified in a multi-GPU environment

    Fixed File

    • Relative Path: ComfyUI/custom_nodes/ComfyUI-nunchaku/nodes/models/flux.py

    • Absolute Path: D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-nunchaku\nodes\models\flux.py

    Code Before Fix

    def load_model(self, model_path, attention, cache_threshold, cpu_offload, device_id, data_type, **kwargs):
        device = torch.device(f"cuda:{device_id}")  # ← Error occurs here
        ...

    Code After Fix

    def load_model(self, model_path, attention, cache_threshold, cpu_offload, device_id, data_type, **kwargs):
        if device_id is None:
            logger.warning("device_id is None, defaulting to 0.")
            device_id = 0
        device = torch.device(f"cuda:{device_id}")
        ...

    Fix Meaning and Effects

    • Error Handling Addition: Explicitly checks if device_id is None and sets the default value to 0 (first GPU)

    • Log Output Improvement: Outputs a warning log for easier problem identification during debugging

    • Operation Stabilization: Processing continues even when device_id is None. Subsequent validation (line 216: device_id >= torch.cuda.device_count() check) also functions normally


    Fix 2: pulid.py - Handling facexlib align face fail Error

    Error Content

    RuntimeError: facexlib align face fail
    
    File "...\nodes\models\pulid.py", line 126, in apply
        id_embedding, _ = pulid_pipline.get_id_embedding(single_image)
    RuntimeError: facexlib align face fail
    

    Error Cause

    • Location: Line 126 of NunchakuFluxPuLIDApplyV2.apply method

    • Root Cause: When calling pulid_pipline.get_id_embedding(single_image), a RuntimeError occurs if face detection/alignment fails. No error handling existed, causing the entire process to abort.

    • Occurrence Conditions:

      • When no face is present in the image

      • When the face is too small or too large

      • When the face angle is too extreme to be detected

      • When image quality is too low for the face detection algorithm

      • When processing multiple images in a batch and face detection fails for some images

    Fixed File

    • Relative Path: ComfyUI/custom_nodes/ComfyUI-nunchaku/nodes/models/pulid.py

    • Absolute Path: D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-nunchaku\nodes\models\pulid.py

    Code Before Fix

    all_embeddings = []
    for i in range(image.shape[0]):
        single_image = image[i : i + 1].squeeze().cpu().numpy() * 255.0
        single_image = np.clip(single_image, 0, 255).astype(np.uint8)
    
        id_embedding, _ = pulid_pipline.get_id_embedding(single_image)  # ← Error occurs here
        if id_embedding is not None:
            all_embeddings.append(id_embedding)

    Code After Fix

    all_embeddings = []
    for i in range(image.shape[0]):
        single_image = image[i : i + 1].squeeze().cpu().numpy() * 255.0
        single_image = np.clip(single_image, 0, 255).astype(np.uint8)
    
        try:
            id_embedding, _ = pulid_pipline.get_id_embedding(single_image)
            if id_embedding is not None:
                all_embeddings.append(id_embedding)
        except RuntimeError as e:
            if "facexlib align face fail" in str(e):
                logger.warning(f"Nunchaku PuLID: Face detection/alignment failed for image {i+1}. Skipping this image.")
            else:
                raise

    Fix Meaning and Effects

    • Error Handling Addition: Catches RuntimeError with a try-except block. Only continues processing when the error message contains "facexlib align face fail"

    • Partial Processing Continuation: Skips images where face detection failed and continues processing other images. The entire batch is not interrupted even if some images fail

    • Log Output Improvement: Clearly records which image failed face detection (image {i+1})

    • Protection for Other Errors: Other RuntimeErrors are re-raised (raise), ensuring unexpected errors propagate properly for debugging


    Fix 3: pulid.py - isinstance → type().name to Resolve Python Class Identity Issue

    Error Content

    TypeError: Nunchaku PuLID Apply V2: Expected ComfyFluxWrapper, but got ComfyFluxWrapper.
    
    File "...\nodes\models\pulid.py", line 145, in apply
        raise TypeError(
    TypeError: Nunchaku PuLID Apply V2: Expected ComfyFluxWrapper, but got ComfyFluxWrapper.
    

    Note: The error message says "Expected ComfyFluxWrapper, but got ComfyFluxWrapper" — the expected type and actual type names are identical. This is a problem that the previous Fix 3 (which simply added error messages to isinstance checks) could not resolve.

    Error Cause

    • Location: Line 143 of NunchakuFluxPuLIDApplyV2.apply method, and line 219 of NunchakuPuLIDLoaderV2.load method

    • Root Cause: Python class identity issue. ComfyUI's custom node loading system can import the same module under different Python module paths:

      1. # Example: the same class loaded via different paths becomes separate class objects in Python custom_nodes.ComfyUI-nunchaku.wrappers.flux.ComfyFluxWrapper ← Class A ComfyUI-nunchaku.wrappers.flux.ComfyFluxWrapper ← Class B (same code, different object) isinstance(obj_from_A, class_B) # → False (same name, but different class objects)

    • Occurrence Conditions:

      • When using the unofficial loader (ComfyUI-nunchaku-unofficial-loader) alongside official nodes (ComfyUI-nunchaku)

      • When ComfyUI's module loading order generates different module paths

      • When sys.path manipulation causes the same module to be imported multiple times

    Previous Fix (Insufficient)

    The previous fix replaced assert isinstance(...) with if not isinstance(...) and improved error messages:

    # Previous fix: still uses isinstance → does NOT resolve class identity issue
    model_wrapper = model.model.diffusion_model
    if not isinstance(model_wrapper, ComfyFluxWrapper):
        actual_type = type(model_wrapper).__name__
        raise TypeError(
            f"Expected ComfyFluxWrapper, but got {actual_type}."
        )

    This improved error messages but did not fix the fundamental problem where isinstance() returns False due to class identity mismatch. The result was the contradictory error message: "Expected ComfyFluxWrapper, but got ComfyFluxWrapper".

    Fixed File

    • Relative Path: ComfyUI/custom_nodes/ComfyUI-nunchaku/nodes/models/pulid.py

    • Absolute Path: D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-nunchaku\nodes\models\pulid.py

    Code After Fix

    3-1. apply method (line 143)

    model_wrapper = model.model.diffusion_model
    if type(model_wrapper).__name__ != "ComfyFluxWrapper":
        actual_type = type(model_wrapper).__name__
        raise TypeError(
            f"Nunchaku PuLID Apply V2: Expected ComfyFluxWrapper, but got {actual_type}. "
            f"This may occur when using quantized models. Please ensure you are using the correct model loader. "
            f"Note: PuLID Apply requires ComfyFluxWrapper. Please use Nunchaku FLUX DiT Loader to load the model."
        )

    3-2. load method (line 224)

    model_wrapper = model.model.diffusion_model
    if type(model_wrapper).__name__ != "ComfyFluxWrapper":
        actual_type = type(model_wrapper).__name__
        if hasattr(model_wrapper, 'model'):
            logger.warning(
                f"Nunchaku PuLID Loader V2: Expected ComfyFluxWrapper, but got {actual_type}. "
                f"Attempting to use 'model' attribute directly."
            )
            transformer = model_wrapper.model
        else:
            raise TypeError(
                f"Nunchaku PuLID Loader V2: Expected ComfyFluxWrapper, but got {actual_type}. "
                f"This may occur when using quantized models. Please ensure you are using the correct model loader."
            )
    else:
        transformer = model_wrapper.model

    Fix Meaning and Effects

    • Class Identity Issue Resolved: Uses type().__name__ instead of isinstance(). Compares class name strings rather than class object identity, making it resilient to module path differences.

    • Comparison with Previous Fix:

    画像

    Fix 4: pulid.py - copy_with_ctx Fallback Using model.clone()

    Error Content

    AttributeError: 'ComfyFluxWrapper' object has no attribute 'ctx_for_copy'
    
    File "...\nodes\models\pulid.py", line 151, in apply
        ret_model_wrapper, ret_model = copy_with_ctx(model_wrapper)
    File "...\wrappers\flux.py", line 350, in copy_with_ctx
        ctx_for_copy = model_wrapper.ctx_for_copy
    AttributeError: 'ComfyFluxWrapper' object has no attribute 'ctx_for_copy'
    

    Note: This error was discovered after applying Fix 3. Once the isinstance check passed, copy_with_ctx was reached, which then failed due to the missing ctx_for_copy attribute.

    Error Cause

    • Location: Line 151 of NunchakuFluxPuLIDApplyV2.apply, calling copy_with_ctx in wrappers/flux.py line 350

    • Root Cause: The copy_with_ctx function depends on the ctx_for_copy attribute of the ComfyFluxWrapper object, but the unofficial loader creates ComfyFluxWrapper without setting this attribute:

      1. # Official NunchakuFluxDiTLoader: model.diffusion_model = ComfyFluxWrapper( transformer, config=comfy_config["model_config"], ctx_for_copy={ # ← Sets ctx_for_copy "comfy_config": comfy_config, "model_config": model_config, "device": device, "device_id": device_id, }, ) # Unofficial loader: # Does NOT pass ctx_for_copy, so nn.Module's __getattr__ raises AttributeError

    • Occurrence Conditions:

      • When the model is loaded by the unofficial loader (ComfyUI-nunchaku-unofficial-loader)

      • When ComfyFluxWrapper is initialized without the ctx_for_copy parameter

    Fixed File

    • Relative Path: ComfyUI/custom_nodes/ComfyUI-nunchaku/nodes/models/pulid.py

    • Absolute Path: D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-nunchaku\nodes\models\pulid.py

    Code Before Fix

    ret_model_wrapper, ret_model = copy_with_ctx(model_wrapper)

    Code After Fix

    if hasattr(model_wrapper, 'ctx_for_copy') and model_wrapper.ctx_for_copy:
        ret_model_wrapper, ret_model = copy_with_ctx(model_wrapper)
    else:
        # Fallback: use ComfyUI's ModelPatcher.clone() for unofficial loaders
        ret_model = model.clone()
        ret_model_wrapper = ret_model.model.diffusion_model

    Fix Meaning and Effects

    • Fallback Added: When ctx_for_copy does not exist, uses ComfyUI's ModelPatcher.clone() method — the official model duplication approach provided by ComfyUI

    • Official Loader Compatibility Maintained: When ctx_for_copy exists, the original copy_with_ctx is used as before

    • Trial and Error History:

    画像
    • model.clone() vs copy_with_ctx Comparison:

    画像

    Fix 5: lora/flux.py - isinstance → type().name to Resolve Class Identity Issue

    Error Content

    AssertionError
    
    File "...\nodes\lora\flux.py", line 112, in load_lora
        assert isinstance(model_wrapper, ComfyFluxWrapper)
    AssertionError
    

    Error Cause

    • Location: Line 112 of NunchakuFluxLoraLoader.load_lora method, and line 250 of NunchakuFluxLoraStack.load_lora_stack method

    • Root Cause: Same Python class identity issue as Fix 3. The LoRA loader also used isinstance checks, which fail in the same way

    • Occurrence Conditions: Same as Fix 3

    Fixed File

    • Relative Path: ComfyUI/custom_nodes/ComfyUI-nunchaku/nodes/lora/flux.py

    • Absolute Path: D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-nunchaku\nodes\lora\flux.py

    Code Before Fix

    5-1. load_lora method (line 112)

    model_wrapper = model.model.diffusion_model
    assert isinstance(model_wrapper, ComfyFluxWrapper)

    5-2. load_lora_stack method (line 250)

    model_wrapper = model.model.diffusion_model
    assert isinstance(model_wrapper, ComfyFluxWrapper)

    Code After Fix

    5-1. load_lora method (lines 112–115)

    model_wrapper = model.model.diffusion_model
    assert type(model_wrapper).__name__ == "ComfyFluxWrapper", (
        f"Expected ComfyFluxWrapper, but got {type(model_wrapper).__name__}. "
        f"Please use Nunchaku FLUX DiT Loader to load the model."
    )

    5-2. load_lora_stack method (lines 253–256)

    model_wrapper = model.model.diffusion_model
    assert type(model_wrapper).__name__ == "ComfyFluxWrapper", (
        f"Expected ComfyFluxWrapper, but got {type(model_wrapper).__name__}. "
        f"Please use Nunchaku FLUX DiT Loader to load the model."
    )

    Fix Meaning and Effects

    • Same Fix as Fix 3 Applied to LoRA Loader: The class identity issue is not limited to pulid.py, so the LoRA loader was fixed in the same way

    • Error Message Added: The original assert isinstance(...) generated a message-less AssertionError. The fix includes a specific error message indicating what went wrong


    Overall Picture and Interrelationships

    Fix Dependency Chain

    Fix 1 (device_id)         Independent fix
    Fix 2 (facexlib)           Independent fix
    Fix 3 (isinstance)      → Fix 4 (copy_with_ctx) cascading dependency
                               Fix 4's error location is only reached after Fix 3 passes
    Fix 5 (lora isinstance)   Same root cause as Fix 3
    

    Discovery Order and Causal Chain

    1. Fix 3 applied → isinstance check now passes

    2. After Fix 3 passes, Fix 4's error newly surfaces → ctx_for_copy does not exist

    3. Fix 4 (1st attempt: deepcopy) tried → fails because ModelPatcher's internal structure cannot be deepcopied

    4. Fix 4 (2nd attempt: model.clone()) → succeeds

    Comparison with Previous Fixes

    画像

    Backward Compatibility

    All fixes maintain backward compatibility:

    • No impact on existing normal operation paths

    • No behavior change when using the official loader

    • Only error cases see improved behavior

    • Existing workflows work without changes

    Recommended Testing

    Fix 3, 4, 5 Testing (New Fixes)

    • Verify PuLID works correctly when the model is loaded via the unofficial loader (ComfyUI-nunchaku-unofficial-loader)

    • Verify PuLID works correctly when the model is loaded via the official loader (NunchakuFluxDiTLoader) — regression test

    • Verify LoRA works correctly when the model is loaded via the unofficial loader

    • Verify workflows mixing unofficial loader and official nodes work correctly

    Summary

    The five fixes resolve the following issues:

    画像

    These fixes ensure the ComfyUI-nunchaku package operates stably with both official and unofficial loaders.

     
     

    GJL

     
     
    Road bike&Cycling Road Race, Respecting Mr.Greg LeMond, & Anti Doping, Football, Rock&Pops, History.

    あなたへのおすすめ