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:
raiseFix 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:
# 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.modelFix 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:
# 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_modelFix 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
Fix 3 applied → isinstance check now passes
After Fix 3 passes, Fix 4's error newly surfaces → ctx_for_copy does not exist
Fix 4 (1st attempt: deepcopy) tried → fails because ModelPatcher's internal structure cannot be deepcopied
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.
