見出し画像

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.

いいなと思ったら応援しよう!