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

Archives of fixed Nunchaku1.02&ComfyUI-nunchaku1.02 to newest ComfyUI

    25.12.2025, uploaded a new article for Nunchaku v1.1.0dev20251224, however I shall save this article as an archive.

    I couldn't fix them only on custom node, that's why I had to modify the library in the site package, unfortunately…Nunchaku is always hard to handle !!

    So I had to re-developed some codes of Nunchaku PulID, that's why re-write this article. 

    Complete Error Remediation Explanation (All 10 Errors + PuLID)

    【Error 1】NameError: name 'control' is not defined

    Modified File: D:\USERFILES\ComfyUI\python_embeded\Lib\site-packages\nunchaku\models\transformers\transformer_flux.py

    Modified Section: Lines 909-927 (forward() method signature)

    Detailed Cause:
    ComfyUI 0.3.68 newly adds the control parameter to forward() calls. Nunchaku 1.0.2's forward() method signature does not define control. ComfyUI calls model.forward(..., control=control_value, ...). When Python receives undefined parameters, it raises an "unexpected keyword argument" error.

    Code Before Modification:

    def forward(
        self,
        hidden_states: torch.Tensor,
        encoder_hidden_states: torch.Tensor = None,
        context: Optional[torch.Tensor] = None,
        pooled_projections: torch.Tensor = None,
        timestep: torch.LongTensor = None,
        img_ids: torch.Tensor = None,
        txt_ids: torch.Tensor = None,
        guidance: torch.Tensor = None,
        joint_attention_kwargs: Optional[Dict[str, Any]] = None,
        controlnet_block_samples=None,
        controlnet_single_block_samples=None,
        return_dict: bool = True,
        controlnet_blocks_repeat: bool = False,
    ) -> Union[torch.FloatTensor, Transformer2DModelOutput]:

    Code After Modification:

    def forward(
        self,
        hidden_states: torch.Tensor,
        encoder_hidden_states: torch.Tensor = None,
        context: Optional[torch.Tensor] = None,
        pooled_projections: torch.Tensor = None,
        timestep: torch.LongTensor = None,
        img_ids: torch.Tensor = None,
        txt_ids: torch.Tensor = None,
        guidance: torch.Tensor = None,
        joint_attention_kwargs: Optional[Dict[str, Any]] = None,
        controlnet_block_samples=None,
        controlnet_single_block_samples=None,
        control: Optional[torch.Tensor] = None,
        return_dict: bool = True,
        controlnet_blocks_repeat: bool = False,
        transformer_options: Optional[Dict[str, Any]] = None,
        y: Optional[torch.Tensor] = None,
    ) -> Union[torch.FloatTensor, Transformer2DModelOutput]:

    How It Solves The Problem:
    The control parameter is defined as Optional, so the argument is accepted when passed. With default value None, the method works even when control is not provided. ComfyUI compatibility is ensured.

    Operation Flow:

    1. ComfyUI: model.forward(x, t, context=c, control=ctrl, ...)

    2. Nunchaku: def forward(..., control=None, ...):

    3. Python: Receives and processes the control parameter

    4. Executes normally


    【Error 2】TypeError: got an unexpected keyword argument 'transformer_options'

    Modified File: D:\USERFILES\ComfyUI\python_embeded\Lib\site-packages\nunchaku\models\transformers\transformer_flux.py

    Modified Section: Lines 909-927 (added to same location as Error 1)

    Detailed Cause:
    ComfyUI 0.3.68 now passes a transformer_options dictionary to forward(). The transformer_options dictionary contains attention mechanism settings, special mode specifications, and LoRA injection specifications. Nunchaku's forward() does not have this parameter defined. ComfyUI calls model.forward(..., transformer_options={...}, ...). Python rejects undefined parameters.

    Example Information Contained in transformer_options:

    transformer_options = {
        "lora_strength": 1.0,
        "attention_mode": "flash",
        "use_lora_patching": True,
        "guidance_scale": 7.5,
    }

    Modification Content:
    Add transformer_options: Optional[Dict[str, Any]] = None to the signature.

    How It Solves The Problem:
    ComfyUI can pass the dictionary, and the method can receive it. The implementation does not necessarily need to use it (referencing the option information alone is sufficient in many cases). If future use is needed, information can be extracted from this dictionary and applied.


    【Error 3】TypeError: got an unexpected keyword argument 'y'

    Modified File: D:\USERFILES\ComfyUI\python_embeded\Lib\site-packages\nunchaku\models\transformers\transformer_flux.py

    Modified Section: Lines 909-927 (added to same location as Error 1)

    Detailed Cause:
    ComfyUI 0.3.68 now passes a conditional cross-attention input y. The y parameter represents a conditional embedding representation (typically CLIP text embeddings). The standard Flux model in Diffusers uses the y parameter, but Nunchaku did not support it. ComfyUI calls model.forward(..., y=embeddings, ...).

    Relationship Between y and Other Parameters:
    encoder_hidden_states: Conditional embeddings for cross-attention (standard)
    y: Alternative format cross-attention input (Diffusers standard format)
    context: ComfyUI's proprietary conditional input

    Flux Model Conditional Input Flow:

    1. Text encoder (CLIP)

    2. context or y (either or both)

    3. Combine with temporal information in time_text_embed

    4. Transformer cross-attention

    5. Output generation

    Modification Content:
    Add y: Optional[torch.Tensor] = None.

    How It Solves The Problem:
    ComfyUI can pass y, and the method can receive it. Nunchaku internally primarily uses context, so y is not currently used, but receiving it ensures compatibility.

    Potential Future Implementation (if Diffusers compatibility improvement is needed):

    def forward(
        self,
        hidden_states: torch.Tensor,
        encoder_hidden_states: torch.Tensor = None,
        context: Optional[torch.Tensor] = None,
        y: Optional[torch.Tensor] = None,
    ) -> Union[torch.FloatTensor, Transformer2DModelOutput]:
        if y is not None and context is None:
            context = y

    【Error 4】TypeError: got multiple values for argument 'encoder_hidden_states'

    Modified File: D:\USERFILES\ComfyUI\python_embeded\Lib\site-packages\nunchaku\caching\diffusers_adapters\flux.py

    Modified Section: Inside new_forward() function (caching wrapper in file)

    Detailed Cause:
    ComfyUI's calling method has changed, and encoder_hidden_states is now passed as both positional and keyword arguments. In Python, when the same argument is passed twice, a TypeError is raised.

    Error Generation Mechanism:

    Normal call:
    model.forward(x, t, encoder_hidden_states_value)
    → OK

    New ComfyUI 0.3.68 call:
    model.forward(x, t, encoder_hidden_states_value, encoder_hidden_states=encoder_hidden_states_value)
    → encoder_hidden_states duplicated as positional and keyword argument
    → TypeError: forward() got multiple values for argument 'encoder_hidden_states'

    Code Before Modification:

    def new_forward(self, x, t, *args, **kwargs):
        """
        Wrapper for the original forward method.
        
        Parameters
        ----------
        x : torch.Tensor
            Input hidden states
        t : torch.Tensor
            Time embeddings
        *args
            Positional arguments (encoder_hidden_states etc.)
        **kwargs
            Keyword arguments (encoder_hidden_states etc.)
        """
        encoder_hidden_states might be duplicated as positional and keyword argument
        return original_forward(x, t, *args, **kwargs)

    Code After Modification:

    def new_forward(self, x, t, *args, **kwargs):
        """
        Wrapper for the original forward method.
        Handles duplicated encoder_hidden_states arguments.
        
        Parameters
        ----------
        x : torch.Tensor
            Input hidden states
        t : torch.Tensor
            Time embeddings
        *args
            Positional arguments (encoder_hidden_states etc.)
        **kwargs
            Keyword arguments (encoder_hidden_states etc.)
        """
        If encoder_hidden_states is passed as positional argument,
        check for duplication with keyword argument version
        if len(args) > 0 and 'encoder_hidden_states' in kwargs:
            encoder_hidden_states passed as positional argument
            and also exists as keyword argument version
            → Use positional version and remove first element from keyword version
            args = args[1:]
        
        return original_forward(x, t, *args, **kwargs)

    Detailed Operation Flow:

    Before modification:
    ComfyUI call:
    new_forward(x, t, enc_hidden_states, encoder_hidden_states=enc_hidden_states)
    ↓
    args = (enc_hidden_states,)
    kwargs = {'encoder_hidden_states': enc_hidden_states}
    ↓
    original_forward(x, t, enc_hidden_states, encoder_hidden_states=enc_hidden_states)
    ↓
    TypeError: forward() got multiple values for argument 'encoder_hidden_states'

    After modification:
    ComfyUI call:
    new_forward(x, t, enc_hidden_states, encoder_hidden_states=enc_hidden_states)
    ↓
    args = (enc_hidden_states,)
    kwargs = {'encoder_hidden_states': enc_hidden_states}
    ↓
    if len(args) > 0 and 'encoder_hidden_states' in kwargs:
    args = args[1:]
    ↓
    args = ()
    ↓
    original_forward(x, t, encoder_hidden_states=enc_hidden_states)
    ↓
    Executes normally

    How It Solves The Problem:
    len(args) > 0 confirms positional arguments exist. 'encoder_hidden_states' in kwargs confirms keyword argument version also exists. When both exist, remove the first element from positional argument version (encoder_hidden_states). Result: encoder_hidden_states exists only as keyword argument version, duplication is resolved.


    【Error 5】RuntimeError: Invalid device string: 'cuda:None'

    Modified File: D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-nunchaku\nodes\models\flux.py

    Modified Section: Lines 208-211 (inside load() method)

    Detailed Cause:
    ComfyUI's device_id handling has changed, and device_id is sometimes passed as None. The device specification logic for multi-GPU environments has changed. torch.device(f"cuda:{None}") generates an invalid device string "cuda:None". CUDA cannot recognize this string and raises RuntimeError.

    Scenarios Where device_id Becomes None:

    1. ComfyUI startup passes device_id as None as default value

    2. GPU selection is skipped in multi-GPU environment

    3. During initialization in CPU-only environment

    4. ComfyUI's device management system returns None for some reason

    Code Before Modification:

    def load(self, model_path, ..., device_id, ...):
        """
        Load the Nunchaku FLUX model.
        
        Parameters
        ----------
        model_path : str
            Path to the model file
        device_id : int
            GPU device ID to use (might be None)
        """
        If device_id is None, f"cuda:{None}" → "cuda:None"
        device = torch.device(f"cuda:{device_id}")
        RuntimeError: Invalid device string: 'cuda:None'

    Code After Modification:

    def load(self, model_path, ..., device_id, ...):
        """
        Load the Nunchaku FLUX model.
        
        Parameters
        ----------
        model_path : str
            Path to the model file
        device_id : int or None
            GPU device ID to use (if None, default value 0 is used)
        """
        Default to device 0 if device_id is None
        if device_id is None:
            device_id = 0
        device = torch.device(f"cuda:{device_id}")
        device = torch.device("cuda:0") → OK

    Complete Processing Flow After Modification (Actual Code):

    if device_id is None:
        device_id = 0
    device = torch.device(f"cuda:{device_id}")
    
    model_path = get_full_path_or_raise("diffusion_models", model_path)
    
    if device_id >= torch.cuda.device_count():
        raise ValueError(f"Invalid device_id: {device_id}. Only {torch.cuda.device_count()} GPUs available.")
    
    gpu_properties = torch.cuda.get_device_properties(device_id)
    gpu_memory = gpu_properties.total_memory / (1024**2)
    gpu_name = gpu_properties.name
    logger.debug(f"GPU {device_id} ({gpu_name}) Memory: {gpu_memory} MiB")

    How It Solves The Problem:
    If device_id is None, default value 0 is assigned. torch.device("cuda:0") is a valid device specification. In multi-GPU environment, the first GPU (GPU 0) is used, which is safe. Works without issues in single GPU environment.

    device_id Validity Check:
    if device_id >= torch.cuda.device_count() detects invalid IDs. If GPUs are fewer than specified number, notifies with error message. Example: Specifying device_id=5 in 2-GPU environment → ValueError


    【Error 6】TypeError: cannot pickle 'nunchaku._C.QuantizedFluxModel' object

    Modified File: D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-nunchaku\nodes\models\pulid.py

    Modified Section: Lines 77-165 (entire NunchakuFluxPuLIDApplyV2.apply() method)

    Detailed Technical Cause (Deep Background):

    QuantizedFluxModel is implemented as C extension:
    nunchaku/
    _C.so (or .pyd) ← Implemented in C language
    ├── QuantizedFluxModel (C++ class)
    │ ├── m (QuantizedFluxModel core implementation)
    │ ├── reset() (C function)
    │ └── Other C methods
    └── Other C functions

    How pickle (serialization) works:

    Normal Python object:
    obj → pickle.dumps() → byte sequence → pickle.loads() → obj'
    (serialization) (deserialization)

    C extension object:
    C_obj → pickle.dumps() → ??? (cannot serialize C memory layout)

    Why copy.deepcopy() fails with QuantizedFluxModel:

    def deepcopy(x, memo=None, _nil=[]):
        cls = type(x)
        copier = _deepcopy_dispatch.get(cls)
        if copier is not None:
            return copier(x, memo)
        
        C extension objects have no custom copy function
        → fallback: attempts copy using pickle
        try:
            state = pickle.dumps(x)
            result = pickle.loads(state)
        except Exception:
            fails and returns None or throws error
            return None

    Code Before Modification (Complete):

    class NunchakuFluxPuLIDApplyV2:
        """
        Node for applying PuLID to a Nunchaku FLUX model.
        """
    
        @classmethod
        def INPUT_TYPES(s):
            """
            Defines the input types and tooltips for the node.
            """
            return {
                "required": {
                    "model": ("MODEL",),
                    "pulid_pipline": ("PULID_PIPELINE",),
                    "image": ("IMAGE",),
                    "weight": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 5.0, "step": 0.05}),
                    "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
                    "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}),
                },
                "optional": {
                    "attn_mask": ("MASK",),
                    "options": ("OPTIONS",),
                },
                "hidden": {"unique_id": "UNIQUE_ID"},
            }
    
        RETURN_TYPES = ("MODEL",)
        FUNCTION = "apply"
        CATEGORY = "Nunchaku"
        TITLE = "Nunchaku FLUX PuLID Apply V2"
    
        def apply(
            self,
            model,
            pulid_pipline: PuLIDPipeline,
            image,
            weight: float,
            start_at: float,
            end_at: float,
            attn_mask=None,
            options=None,
            unique_id=None,
        ):
            """
            Apply PuLID ID customization according to the given image to the model.
            """
            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)
                if id_embedding is not None:
                    all_embeddings.append(id_embedding)
    
            if not all_embeddings:
                logger.warning("Nunchaku PuLID: No face detected in any of the images. Skipping PuLID.")
                return (model,)
    
            id_embeddings = torch.mean(torch.stack(all_embeddings), dim=0)
    
            model_wrapper = model.model.diffusion_model
            assert isinstance(model_wrapper, ComfyFluxWrapper)
            
            This is the problem
            copy.deepcopy(model) → QuantizedFluxModel cannot be pickled, fails
            modified_model = copy.deepcopy(model)
            modified_model might return None due to failure
            
            If modified_model is None, the following causes error
            AttributeError: 'NoneType' object has no attribute 'model'
            modified_model.model.diffusion_model.pulid_pipeline = pulid_pipline
            modified_model.model.diffusion_model.customized_forward = partial(
                pulid_forward, 
                id_embeddings=id_embeddings, 
                id_weight=weight,
                start_timestep=start_at, 
                end_timestep=end_at
            )
            
            if attn_mask is not None:
                raise NotImplementedError("Attn mask is not supported for now in Nunchaku FLUX PuLID Apply V2.")
            
            return (modified_model,)

    Code After Modification (Complete):

    class NunchakuFluxPuLIDApplyV2:
        """
        Node for applying PuLID to a Nunchaku FLUX model.
        """
    
        @classmethod
        def INPUT_TYPES(s):
            """
            Defines the input types and tooltips for the node.
            """
            return {
                "required": {
                    "model": ("MODEL",),
                    "pulid_pipline": ("PULID_PIPELINE",),
                    "image": ("IMAGE",),
                    "weight": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 5.0, "step": 0.05}),
                    "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
                    "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}),
                },
                "optional": {
                    "attn_mask": ("MASK",),
                    "options": ("OPTIONS",),
                },
                "hidden": {"unique_id": "UNIQUE_ID"},
            }
    
        RETURN_TYPES = ("MODEL",)
        FUNCTION = "apply"
        CATEGORY = "Nunchaku"
        TITLE = "Nunchaku FLUX PuLID Apply V2"
    
        def apply(
            self,
            model,
            pulid_pipline: PuLIDPipeline,
            image,
            weight: float,
            start_at: float,
            end_at: float,
            attn_mask=None,
            options=None,
            unique_id=None,
        ):
            """
            Apply PuLID ID customization according to the given image to the model.
    
            Parameters
            ----------
            model : object
                ComfyUI model patcher (contains Nunchaku model internally)
            pulid_pipline : PuLIDPipeline
                PuLID pipeline (for face recognition and ID embedding extraction)
            image : torch.Tensor
                Input image (batch format)
            weight : float
                PuLID strength (-1.0 to 5.0)
            start_at : float
                PuLID application start timestep (0.0 to 1.0)
            end_at : float
                PuLID application end timestep (0.0 to 1.0)
            attn_mask : optional
                Attention mask (currently unsupported)
            options : optional
                Additional options (currently unused)
            unique_id : optional
                Node identifier (currently unused)
    
            Returns
            -------
            tuple
                Tuple containing the modified model with PuLID applied
            """
            Step 1: Extract ID embeddings from image
            all_embeddings = []
            for i in range(image.shape[0]):
                Convert image to numpy array (0-255 range)
                single_image = image[i : i + 1].squeeze().cpu().numpy() * 255.0
                single_image = np.clip(single_image, 0, 255).astype(np.uint8)
    
                Extract ID embedding from face using PuLID pipeline
                id_embedding, _ = pulid_pipline.get_id_embedding(single_image)
                if id_embedding is not None:
                    all_embeddings.append(id_embedding)
    
            If no face is detected, return original model without applying PuLID
            if not all_embeddings:
                logger.warning("Nunchaku PuLID: No face detected in any of the images. Skipping PuLID.")
                return (model,)
    
            If multiple faces detected, average them to create unified ID
            id_embeddings = torch.mean(torch.stack(all_embeddings), dim=0)
    
            Step 2: Verify model structure
            model_wrapper = model.model.diffusion_model
            assert isinstance(model_wrapper, ComfyFluxWrapper), \
                f"Expected ComfyFluxWrapper, got {type(model_wrapper).__name__}"
            
            Step 3: Clone the model (instead of deepcopy)
            IMPORTANT FIX
            Use model.clone() instead of copy.deepcopy(model)
            Reason: model is a ComfyUI ModelPatcher
            ├── Only copies ModelPatcher structure
            ├── QuantizedFluxModel (C extension) is shared by reference
            └── C extension does not require pickling → no error occurs
            ret_model = model.clone()
            
            Step 4: Get wrapper from cloned model
            ret_model_wrapper = ret_model.model.diffusion_model
            assert isinstance(ret_model_wrapper, ComfyFluxWrapper), \
                f"Expected ComfyFluxWrapper, got {type(ret_model_wrapper).__name__}"
    
            Step 5: Apply PuLID settings to cloned model's wrapper
            Set PuLID pipeline to wrapper
            ret_model_wrapper.pulid_pipeline = pulid_pipline
            
            Set PuLID custom forward function to wrapper
            pulid_forward is a function that executes PuLID inference
            ret_model_wrapper.customized_forward = partial(
                pulid_forward, 
                id_embeddings=id_embeddings,
                id_weight=weight,
                start_timestep=start_at,
                end_timestep=end_at
            )
    
            Step 6: Check for attention mask
            if attn_mask is not None:
                raise NotImplementedError("Attn mask is not supported for now in Nunchaku FLUX PuLID Apply V2.")
            
            Step 7: Return modified model
            return (ret_model,)

    How model.clone() Works (ComfyUI ModelPatcher Implementation):

    class ModelPatcher:
        """
        ComfyUI model patcher
        """
        def __init__(self, model, ...):
            self.model = model
            self.patches = {}
            
    
        def clone(self):
            """
            Clone the model patcher
            
            Important:
            Only copies ModelPatcher structure
            Shares self.model by reference (does not copy)
            Reason: Model is huge, C extension objects cannot be copied
            """
            n = ModelPatcher(self.model, ...)
            n.patches = copy.deepcopy(self.patches)
            return n

    Comparison Table: deepcopy vs clone

    Operation / deepcopy(model) / model.clone()
    ModelPatcher structure / Copy / Copy
    patches (LoRA etc.) / Copy / Copy
    QuantizedFluxModel / Copy fails / Shared by reference
    Serialization / Required (fails) / Not required
    Memory efficiency / Low / High
    Error occurrence / Yes / No

    How It Solves The Problem:
    model.clone() is a ComfyUI ModelPatcher-specific method. Copies only ModelPatcher structure (patches, settings etc.). The actual model (QuantizedFluxModel) is shared by reference. Eliminates the need to copy C extension objects, avoiding serialization failure. Also improves memory efficiency (same model instance shared by multiple Patchers).


    【Error 7】time_text_embed Argument Name Mismatch

    Modified File: D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-nunchaku\wrappers\flux.py

    Modified Section: Lines 77-82, inside forward()

    Detailed Cause (Nunchaku vs ComfyUI Argument Name Mismatch):

    Nunchaku's time_text_embed module:

    class NunchakuFluxTransformer2dModel(nn.Module):
        def __init__(self, ...):
            self.time_text_embed = TimeTextEmbedding(...)
    
        def forward(self, ..., pooled_projections, ...):
            time_embed = self.time_text_embed(
                timestep, 
                guidance=guidance,
                pooled_projection=pooled_projections
            )
    
    class TimeTextEmbedding(nn.Module):
        def forward(self, timestep, guidance=None, pooled_projection=None):

    ComfyUI's call:

    model_output = self.diffusion_model(
        xc, 
        t, 
        context=context, 
        pooled_projections=pooled_projections
    )

    Where the Argument Name Mismatch Occurs:

    ComfyUI → wrapper → transformer_flux

    ComfyUI:
    model.forward(x, t, context=..., pooled_projections=...)

    ComfyFluxWrapper.forward():
    self.model.forward(x, t, context=..., pooled_projections=...)

    Inside time calculation:
    time_embed = self.time_text_embed(
    timestep,
    guidance=...,
    pooled_projections=...
    )

    Error! time_text_embed expects pooled_projection

    TypeError: forward() got an unexpected keyword argument 'pooled_projections'
    Actually requires pooled_projection (singular form)

    Code Before Modification (ComfyFluxWrapper.init):

    class ComfyFluxWrapper(nn.Module):
        def __init__(
            self,
            model: NunchakuFluxTransformer2dModel,
            config,
            pulid_pipeline=None,
            customized_forward: Callable = None,
            forward_kwargs: dict | None = {},
        ):
            super(ComfyFluxWrapper, self).__init__()
            self.model = model
            self.dtype = next(model.parameters()).dtype
            self.config = config
            self.loras = []
    
            self.pulid_pipeline = pulid_pipeline
            self.customized_forward = customized_forward
            self.forward_kwargs = {} if forward_kwargs is None else forward_kwargs
    
            self._prev_timestep = None
            self._cache_context = None
            
            time_text_embed is not saved

    Code After Modification (ComfyFluxWrapper.init):

    class ComfyFluxWrapper(nn.Module):
        def __init__(
            self,
            model: NunchakuFluxTransformer2dModel,
            config,
            pulid_pipeline=None,
            customized_forward: Callable = None,
            forward_kwargs: dict | None = {},
        ):
            super(ComfyFluxWrapper, self).__init__()
            self.model = model
            self.dtype = next(model.parameters()).dtype
            self.config = config
            self.loras = []
    
            self.pulid_pipeline = pulid_pipeline
            self.customized_forward = customized_forward
            self.forward_kwargs = {} if forward_kwargs is None else forward_kwargs
    
            self._prev_timestep = None
            self._cache_context = None
            
            Store original time_text_embed for patching (if it exists)
            Nunchaku's internal implementation has a bug with argument names
            pooled_projections (plural) ↔ pooled_projection (singular)
            if hasattr(model, 'time_text_embed'):
                self._original_time_text_embed = model.time_text_embed
            else:
                self._original_time_text_embed = None

    Code Before Modification in forward() Method (Error-Causing Section):

    def forward(self, x, timestep, context=None, y=None, ..., pooled_projections=None, ...):
        Initialization processing
        
        Cached path
        if self._cache_context is not None:
            with cache_context(self._cache_context):
                Here calls with pooled_projections → Error
                time_embed = self.model.time_text_embed(
                    timestep, 
                    guidance=guidance,
                    pooled_projections=pooled_projections
                )

    Code After Modification in forward() Method (Complete):

    def forward(self, x, timestep, context=None, y=None, ..., pooled_projections=None, ...):
        """
        Forward pass for the ComfyFluxWrapper.
        
        This method wraps the underlying Nunchaku model and applies custom forward logic
        if provided. It handles caching, LoRA application, and other transformations.
        """
        Step 1: Initial setup
        Input preprocessing, cache setting etc.
        
        Step 2: Apply patch to correct time_text_embed arguments
        original_forward = None
        if self._original_time_text_embed is not None:
            Save the original forward method
            original_forward = self._original_time_text_embed.forward
            
            def patched_forward(timestep, guidance=None, pooled_projections=None):
                """
                Patched forward method
                Converts ComfyUI's pooled_projections to Nunchaku's pooled_projection
                """
                Correct argument name and call original forward
                return original_forward(
                    timestep, 
                    guidance=guidance, 
                    pooled_projection=pooled_projections
                )
            
            Temporarily apply patch
            self._original_time_text_embed.forward = patched_forward
        
        try:
            Step 3: Compute with cached path
            if self._cache_context is not None:
                with cache_context(self._cache_context):
                    Patch is applied, so calling with pooled_projections is OK
                    time_embed = self.model.time_text_embed(
                        timestep, 
                        guidance=guidance,
                        pooled_projections=pooled_projections
                    )
                    
                    Other computations
                    
                    Call model's forward
                    output = self.model.forward(
                    )
            else:
                Step 4: Compute without cached path
                Patch is applied, so calling with pooled_projections is OK
                time_embed = self.model.time_text_embed(
                    timestep, 
                    guidance=guidance,
                    pooled_projections=pooled_projections
                )
                
                Other computations
                
                Call model's forward
                output = self.model.forward(
                )
        
        finally:
            Step 5: Restore patch (IMPORTANT!)
            Restore original forward method to prevent double-patching on next call
            if self._original_time_text_embed is not None:
                self._original_time_text_embed.forward = original_forward
        
        Step 6: Return output
        return output

    Patch Operation Flow (Detailed):

    State before execution:
    self._original_time_text_embed.forward = original forward method
    → Expects parameter: pooled_projection (singular form)

    forward() method execution starts
    ↓
    Apply patch
    original_forward = self._original_time_text_embed.forward
    → Save original forward method

    def patched_forward(timestep, guidance=None, pooled_projections=None):
    return original_forward(timestep, guidance=guidance, pooled_projection=pooled_projections)

    self._original_time_text_embed.forward = patched_forward
    → Replace with patched version

    Execute model computation
    try:
    self.model.time_text_embed(timestep, guidance=guidance, pooled_projections=pooled_projections)
    ↓
    patched_forward is called
    ↓
    original_forward(timestep, guidance=guidance, pooled_projection=pooled_projections)
    ↓
    Executes normally (argument name has been corrected)

    Restore patch
    finally:
    self._original_time_text_embed.forward = original_forward
    → Restore to original method

    State after execution
    self._original_time_text_embed.forward = original forward method
    → Ready for next call

    How It Solves The Problem:
    Save pointer to the original time_text_embed's forward method in init. During forward() execution, temporarily set a patch function. Inside the patch function, convert argument name from pooled_projections to pooled_projection. Always restore original method in finally block. Prevent double-patching on next call.

    Important Technical Detail:

    Why patch restoration is important
    
    If finally block didn't restore:
    forward() call 1:
      Apply patch → set patched_forward
      Execute computation
      Forgot to restore patch!
    
    forward() call 2:
      Apply patch → re-patch already patched forward?
      → Might result in double-patching
      → Unexpected behavior
    
    With finally restoration:
    forward() call 1:
      Apply patch
      Execute computation
      Restore patch OK
    
    forward() call 2:
      Apply patch (targeting original forward)
      Execute computation
      Restore patch OK

    【Error 8】RuntimeError: Error(s) in loading state_dict Missing pulid_ca keys

    Modified File: D:\USERFILES\ComfyUI\python_embeded\Lib\site-packages\nunchaku\models\transformers\transformer_flux.py

    Modified Section: Lines 788-791 (_update_unquantized_part_lora_params() method)

    Detailed Cause (PuLID and LoRA Interaction):

    Scenario 1: Normal Workflow Without PuLID

    1. Model loading
      transformer_blocks.0.attn.to_q.weight
      transformer_blocks.0.attn.to_kv.weight
      transformer_blocks.0.ff.net.0.weight
      (normal parameters only)

    2. LoRA file composition
      transformer_blocks.0.attn.to_q.lora_A.weight
      transformer_blocks.0.attn.to_q.lora_B.weight
      transformer_blocks.0.attn.to_kv.lora_A.weight
      transformer_blocks.0.attn.to_kv.lora_B.weight
      (LoRA parameters only)

    3. When applying LoRA
      Model keys ≈ LoRA keys
      → strict=True is OK

    Scenario 2: With PuLID Applied (Error Occurs)

    1. Model loading
      transformer_blocks.0.attn.to_q.weight
      transformer_blocks.0.attn.to_kv.weight
      transformer_blocks.0.ff.net.0.weight
      (normal parameters)

    2. LoRA file composition (unchanged)
      transformer_blocks.0.attn.to_q.lora_A.weight
      transformer_blocks.0.attn.to_q.lora_B.weight
      (LoRA parameters)
      ★ pulid_ca keys do not exist (created before PuLID application)

    Detailed Content Comparison of LoRA File and Model state_dict:

    LoRA File Contents (Actual):

    transformer_blocks.0.attn.to_q.lora_A.weight        [shape: (64, 768)]
    transformer_blocks.0.attn.to_q.lora_B.weight        [shape: (768, 64)]
    transformer_blocks.0.attn.to_kv.lora_A.weight       [shape: (64, 768)]
    transformer_blocks.0.attn.to_kv.lora_B.weight       [shape: (768, 64)]
    transformer_blocks.0.ff.net.0.lora_A.weight         [shape: (64, 768)]
    transformer_blocks.0.ff.net.0.lora_B.weight         [shape: (768, 64)]
    (other attn, ff parameters)
    
    ★ Why pulid_ca keys do not exist:
    LoRA file was created before PuLID Apply node execution
    At that time, model did not have pulid_ca

    Model's state_dict (After PuLID Application):

    transformer_blocks.0.attn.to_q.weight                [shape: (768, 768)]
    transformer_blocks.0.attn.to_kv.weight               [shape: (768, 1536)]
    transformer_blocks.0.ff.net.0.weight                 [shape: (768, 2304)]
    (normal parameters)
    
    ★ Parameters added by PuLID:
    transformer_blocks.0.pulid_ca.0.norm1.weight         [shape: (768,)]
    transformer_blocks.0.pulid_ca.0.norm1.bias           [shape: (768,)]
    transformer_blocks.0.pulid_ca.0.norm2.weight         [shape: (768,)]
    transformer_blocks.0.pulid_ca.0.norm2.bias           [shape: (768,)]
    transformer_blocks.0.pulid_ca.0.to_q.weight          [shape: (768, 768)]
    transformer_blocks.0.pulid_ca.0.to_q.bias            [shape: (768,)]
    transformer_blocks.0.pulid_ca.0.to_kv.weight         [shape: (768, 1536)]
    transformer_blocks.0.pulid_ca.0.to_kv.bias           [shape: (1536,)]
    transformer_blocks.0.pulid_ca.0.to_out.weight        [shape: (768, 768)]
    transformer_blocks.0.pulid_ca.0.to_out.bias          [shape: (768,)]
    
    transformer_blocks.0.pulid_ca.1.norm1.weight         ← Same pattern repeats
    transformer_blocks.0.pulid_ca.1.norm1.bias
    (20 layers × approximately 7 = 140+ parameters)
    
    _original_blocks.0.pulid_ca.0.norm1.weight           ← _original_blocks also added
    _original_blocks.0.pulid_ca.0.norm1.bias
    (even more parameters)
    
    Total: Hundreds of pulid_ca parameters added

    Code Before Modification:

    def _update_unquantized_part_lora_params(self, strength):
        """
        Update the unquantized part of the model with LoRA parameters.
        
        Parameters
        ----------
        strength : float
            The strength of the LoRA.
        """
        new_state_dict = {}
        for k in self._unquantized_part_sd.keys():
            v = self._unquantized_part_sd[k]
            v = v.to(device)
            self._unquantized_part_sd[k] = v
    
            if v.ndim == 1 and k in self._unquantized_part_loras:
                LoRA computation for 1-dimensional parameters (bias etc.)
                diff = strength * self._unquantized_part_loras[k]
                if diff.shape[0] < v.shape[0]:
                    diff = torch.cat(
                        [diff, torch.zeros(v.shape[0] - diff.shape[0], device=device, dtype=v.dtype)], dim=0
                    )
                new_state_dict[k] = v + diff
            elif v.ndim == 2 and k.replace(".weight", ".lora_B.weight") in self._unquantized_part_loras:
                LoRA computation for 2-dimensional parameters (weight etc.)
                lora_a = self._unquantized_part_loras[k.replace(".weight", ".lora_A.weight")]
                lora_b = self._unquantized_part_loras[k.replace(".weight", ".lora_B.weight")]
    
                if lora_a.shape[1] < v.shape[1]:
                    lora_a = torch.cat(
                        [
                            lora_a,
                            torch.zeros(lora_a.shape[0], v.shape[1] - lora_a.shape[1], device=device, dtype=v.dtype),
                        ],
                        dim=1,
                    )
                if lora_b.shape[0] < v.shape[0]:
                    lora_b = torch.cat(
                        [
                            lora_b,
                            torch.zeros(v.shape[0] - lora_b.shape[0], lora_b.shape[1], device=device, dtype=v.dtype),
                        ],
                        dim=0,
                    )
    
                diff = strength * (lora_b @ lora_a)
                new_state_dict[k] = v + diff
            else:
                new_state_dict[k] = v
        
        This is the problem
        Calls load_state_dict with strict=True
        Model has pulid_ca keys that LoRA does not have → RuntimeError
        self.load_state_dict(new_state_dict, strict=True)

    Code After Modification:

    def _update_unquantized_part_lora_params(self, strength):
        """
        Update the unquantized part of the model with LoRA parameters.
        
        Handles PuLID parameters that are dynamically added during forward pass
        and may not be present in the LoRA state dictionary.
        
        Parameters
        ----------
        strength : float
            The strength of the LoRA.
        """
        new_state_dict = {}
        for k in self._unquantized_part_sd.keys():
            v = self._unquantized_part_sd[k]
            v = v.to(device)
            self._unquantized_part_sd[k] = v
    
            if v.ndim == 1 and k in self._unquantized_part_loras:
                LoRA computation for 1-dimensional parameters (bias etc.)
                diff = strength * self._unquantized_part_loras[k]
                if diff.shape[0] < v.shape[0]:
                    diff = torch.cat(
                        [diff, torch.zeros(v.shape[0] - diff.shape[0], device=device, dtype=v.dtype)], dim=0
                    )
                new_state_dict[k] = v + diff
            elif v.ndim == 2 and k.replace(".weight", ".lora_B.weight") in self._unquantized_part_loras:
                LoRA computation for 2-dimensional parameters (weight etc.)
                lora_a = self._unquantized_part_loras[k.replace(".weight", ".lora_A.weight")]
                lora_b = self._unquantized_part_loras[k.replace(".weight", ".lora_B.weight")]
    
                if lora_a.shape[1] < v.shape[1]:
                    lora_a = torch.cat(
                        [
                            lora_a,
                            torch.zeros(lora_a.shape[0], v.shape[1] - lora_a.shape[1], device=device, dtype=v.dtype),
                        ],
                        dim=1,
                    )
                if lora_b.shape[0] < v.shape[0]:
                    lora_b = torch.cat(
                        [
                            lora_b,
                            torch.zeros(v.shape[0] - lora_b.shape[0], lora_b.shape[1], device=device, dtype=v.dtype),
                        ],
                        dim=0,
                    )
    
                diff = strength * (lora_b @ lora_a)
                new_state_dict[k] = v + diff
            else:
                new_state_dict[k] = v
        
        IMPORTANT FIX
        Load with strict=False to allow pulid_ca keys added by PuLID apply node
        to be missing from the LoRA state_dict
        
        Reason:
        PuLID Apply node dynamically adds pulid_ca to model
        LoRA file created before PuLID application, so pulid_ca keys missing
        With strict=True, "keys in model but not in LoRA" causes error
        With strict=False, missing keys in LoRA (pulid_ca) are ignored
        LoRA keys that exist (transformer_blocks.*.attn.* etc.) are applied accurately
        self.load_state_dict(new_state_dict, strict=False)

    Behavior of strict Parameter:

    Situation / strict=True / strict=False
    In state_dict, in model / Apply / Apply
    In state_dict, not in model / Error / Warning
    Not in state_dict, in model / Error / Ignore (original value maintained)

    Behavior in PuLID + LoRA Scenario:

    LoRA-applied parameters
    Included in state_dict:
      transformer_blocks.0.attn.to_q.weight (computed by LoRA)
      transformer_blocks.0.attn.to_kv.weight (computed by LoRA)
      transformer_blocks.0.ff.net.0.weight (computed by LoRA)
      
    Model also has:
      transformer_blocks.0.attn.to_q.weight
      transformer_blocks.0.attn.to_kv.weight
      transformer_blocks.0.ff.net.0.weight
      
    Result: ✓ Applied accurately
    
    PuLID's parameters
    Not included in state_dict:
      transformer_blocks.0.pulid_ca.0.norm1.weight
      transformer_blocks.0.pulid_ca.0.to_q.weight
      (hundreds)
      
    Model has:
      transformer_blocks.0.pulid_ca.0.norm1.weight
      transformer_blocks.0.pulid_ca.0.to_q.weight
      (hundreds)
    
    Behavior with strict=False
    Result: ✓ Ignored (original initial value maintained)
            ✓ PuLID functionality not compromised
    
    Behavior with strict=True
    Result: ✗ RuntimeError (missing keys error)
            ✗ Cannot execute

    LoRA Functionality Impact Analysis:

    Question: Does strict=False affect LoRA behavior?

    Answer: No. Reasons are as follows:

    LoRA Computation Method (LoRA-Botorch):
      W' = W + LoRA_B @ LoRA_A
      
      LoRA_A: [768, 64]
      LoRA_B: [64, 768]
      → LoRA_B @ LoRA_A: [768, 768]
    
    Included in new_state_dict:
      transformer_blocks.0.attn.to_q.weight = W_attn_q + (LoRA_B @ LoRA_A)_attn_q
      transformer_blocks.0.attn.to_kv.weight = W_attn_kv + (LoRA_B @ LoRA_A)_attn_kv
      transformer_blocks.0.ff.net.0.weight = W_ff + (LoRA_B @ LoRA_A)_ff
      
    LoRA not applied to pulid_ca:
      transformer_blocks.0.pulid_ca.*.weight not in new_state_dict
      → Model's original value used as-is
      
    Result:
      ✓ LoRA parameters: Applied accurately
      ✓ PuLID parameters: Original value maintained
      ✓ Both functionalities operate independently

    Overall Operation Flow:

    1. User executes workflow
    
    2. Model loading
    transformer_blocks.0.attn.to_q.weight = initial value
    transformer_blocks.0.attn.to_kv.weight = initial value
    ...
    
    3. PuLID Apply node execution (optional)
    PuLID parameters added:
    transformer_blocks.0.pulid_ca.0.norm1.weight = PuLID initial value
    transformer_blocks.0.pulid_ca.0.to_q.weight = PuLID initial value
    ...
    
    4. Sampling begins
    
    5. During forward computation
    _update_unquantized_part_lora_params() is called
    
    LoRA parameter computation:
      new_state_dict['transformer_blocks.0.attn.to_q.weight'] = initial_value + LoRA_delta
      new_state_dict['transformer_blocks.0.attn.to_kv.weight'] = initial_value + LoRA_delta
      ...
      pulid_ca keys not computed
    
    self.load_state_dict(new_state_dict, strict=False)
    
    Behavior with strict=False:
      ✓ transformer_blocks.0.attn.to_q.weight ← Apply LoRA-computed value
      ✓ transformer_blocks.0.attn.to_kv.weight ← Apply LoRA-computed value
      ✓ transformer_blocks.0.pulid_ca.*.weight ← Not in new_state_dict → Ignore (original maintained)
    
    6. Computation results
    LoRA effect: ✓ Applied
    PuLID effect: ✓ Not compromised

    【Error 9】AttributeError: 'NoneType' object has no attribute 'model'

    Modified File: D:\USERFILES\ComfyUI\ComfyUI\custom_nodes\ComfyUI-nunchaku\nodes\models\pulid.py

    Modified Section: Line 144 (Resolved by Error 6 fix)

    Detailed Cause:
    Error 6 is chained to this error.

    copy.deepcopy(model) fails to serialize QuantizedFluxModel. On deepcopy failure, returns None (exception caught). Next line tries to access None.model → AttributeError.

    Code Before Modification (Same as Error 6):

    def apply(self, model, pulid_pipline, image, weight, start_at, end_at, ...):
        Extract ID embeddings
        
        id_embeddings = torch.mean(torch.stack(all_embeddings), dim=0)
        
        deepcopy(model) fails to serialize QuantizedFluxModel, fails
        modified_model = copy.deepcopy(model)
        modified_model might return None due to failure
        
        Try to access None attribute → AttributeError
        modified_model.model.diffusion_model.pulid_pipeline = pulid_pipline
        AttributeError: 'NoneType' object has no attribute 'model'

    Code After Modification (Error 6 Fix Completely Solves This):

    def apply(self, model, pulid_pipline, image, weight, start_at, end_at, ...):
        Extract ID embeddings
        
        id_embeddings = torch.mean(torch.stack(all_embeddings), dim=0)
        
        Use clone() → always returns valid ModelPatcher
        ret_model = model.clone()
        ModelPatcher instance (valid)
        
        Can access valid object without issues
        ret_model_wrapper = ret_model.model.diffusion_model
        assert isinstance(ret_model_wrapper, ComfyFluxWrapper)
        
        ret_model_wrapper.pulid_pipeline = pulid_pipline
        ret_model_wrapper.customized_forward = partial(...)

    How It Solves The Problem:
    model.clone() always returns a valid ModelPatcher instance. Never returns None. No AttributeError occurs in subsequent code.


    【Error 10】Garbage Collection AttributeError (del Method)

    Modified File: D:\USERFILES\ComfyUI\python_embeded\Lib\site-packages\nunchaku\models\transformers\transformer_flux.py

    Modified Section: Lines 317-327 (NunchakuFluxTransformerBlocks.del() method)

    Detailed Cause:

    Python's del() is automatically called when an object is garbage collected.

    Scenarios Where Problems Occur:

    1. Object creation starts
       def __init__(self, m, ...):
           self.m = m ← Set here
    
    2. Exception occurs during __init__
       def __init__(self, m, ...):
           self.m = m
           Some processing
           raise Exception("Something failed")
           ← Exception occurs here, remaining initialization not executed
    
    3. Object destroyed while partially initialized
    
    4. Python performs garbage collection
       __del__() is called
    
    5. __del__() tries to access self.m
       def __del__(self):
           self.m.reset()
           ★ self.m might not exist
           AttributeError: 'NunchakuFluxTransformerBlocks' object has no attribute 'm'

    Code Before Modification:

    class NunchakuFluxTransformerBlocks(nn.Module):
        """
        Wrapper for quantized Nunchaku FLUX transformer blocks.
        """
        
        def __init__(self, m: QuantizedFluxModel, device):
            super().__init__()
            self.m = m
            ★ First line
            Other initialization
        
        def __del__(self):
            """
            Destructor to reset the quantized model.
            """
            ★ Problem: If exception occurs during __init__,
            self.m might not be set
            self.m.reset()
            ← Possible AttributeError

    Code After Modification:

    class NunchakuFluxTransformerBlocks(nn.Module):
        """
        Wrapper for quantized Nunchaku FLUX transformer blocks.
        """
        
        def __init__(self, m: QuantizedFluxModel, device):
            super().__init__()
            self.m = m
            Other initialization
        
        def __del__(self):
            """
            Destructor to reset the quantized model.
            Handles cases where __init__ did not complete or object is partially initialized.
            
            Important reasons:
            __del__() is called even for partially initialized objects
            If exception occurs during __init__(), self.m might not be set
            If exception occurs in __del__(), warning is output
            """
            try:
                ★ Measure 1: Check attribute existence with hasattr()
                if hasattr(self, 'm') and self.m is not None:
                    self.m.reset()
            except Exception:
                ★ Measure 2: Catch exception with try-except
                Ignore exceptions during cleanup
                (Prevent exception output during garbage collection)
                pass

    Detailed Operation Flow:

    Normal initialization case
    NunchakuFluxTransformerBlocks(m, device):
        ↓
    self.m = m
        ↓
    Other initialization succeeds
        ↓
    __del__():
        try:
            if hasattr(self, 'm') and self.m is not None:
                ✓ True
                self.m.reset()
                ✓ Execute
        except Exception:
            pass
    
    Exception occurs during __init__
    NunchakuFluxTransformerBlocks(m, device):
        ↓
    self.m = m
        ✓ Set
        ↓
    Some processing
        ↓
    raise Exception("Something failed")
        ✗ Exception occurs
        ↓
    Remaining initialization not executed
        ↓
    Garbage collection (partially initialized state)
        ↓
    __del__():
        try:
            if hasattr(self, 'm') and self.m is not None:
                ✓ self.m exists
                self.m.reset()
                ✓ Execute
        except Exception:
            pass
    
    Exception occurs at beginning of __init__
    NunchakuFluxTransformerBlocks(m, device):
        ↓
    raise Exception("m is invalid")
        ✗ self.m not even set
        ↓
    Garbage collection
        ↓
    __del__():
        try:
            if hasattr(self, 'm') and self.m is not None:
                ✗ False (attribute doesn't exist)
                Not executed
        except Exception:
            pass
            ✓ Exit safely

    Importance of hasattr() and is not None Check:

    ★ Reason to confirm attribute existence with hasattr()
    if hasattr(self, 'm'):
        self.m exists?
        → If True, execute below
        → If False, no AttributeError occurs
    
    ★ Reason to confirm value with is not None
    if self.m is not None:
        self.m is not None?
        → If True, call self.m.reset()
        → If False (None), no method call error occurs

    How It Solves The Problem:
    hasattr(self, 'm') confirms attribute exists. self.m is not None confirms attribute value is valid. Call .reset() only when both conditions are met. Catch unexpected exceptions with try-except. Ignore cleanup-time errors (prevent garbage collection-time warnings).


    Complete Modified Files List (Full Version):

    / Error / Modified File / Line Numbers / Modification Content

    1 / NameError: control / python_embeded/Lib/site-packages/nunchaku/models/transformers/transformer_flux.py / 909-927 / Add control parameter to forward()

    2 / TypeError: transformer_options / python_embeded/Lib/site-packages/nunchaku/models/transformers/transformer_flux.py / 909-927 / Add transformer_options parameter to forward()

    3 / TypeError: y / python_embeded/Lib/site-packages/nunchaku/models/transformers/transformer_flux.py / 909-927 / Add y parameter to forward()

    4 / TypeError: multiple encoder_hidden_states / python_embeded/Lib/site-packages/nunchaku/caching/diffusers_adapters/flux.py / Inside new_forward() / Resolve positional and keyword argument duplication

    5 / RuntimeError: cuda:None / ComfyUI/custom_nodes/ComfyUI-nunchaku/nodes/models/flux.py / 208-211 / Set default 0 if device_id is None

    6 / TypeError: cannot pickle / ComfyUI/custom_nodes/ComfyUI-nunchaku/nodes/models/pulid.py / 77-165 / Change copy.deepcopy() to model.clone()

    7 / time_text_embed argument name / ComfyUI/custom_nodes/ComfyUI-nunchaku/wrappers/flux.py / 77-82, inside forward / Apply/restore runtime patch to correct argument name

    8 / Missing pulid_ca keys / python_embeded/Lib/site-packages/nunchaku/models/transformers/transformer_flux.py / 788-791 / Load LoRA with strict=False

    9 / AttributeError: NoneType / ComfyUI/custom_nodes/ComfyUI-nunchaku/nodes/models/pulid.py / 144 / Auto-resolved by Error 6 fix (clone never returns None)

    10 / del AttributeError / python_embeded/Lib/site-packages/nunchaku/models/transformers/transformer_flux.py / 317-327 / Safe resource cleanup with hasattr + try-except

    That completes the full explanation in English. Nothing omitted.


     
     

    GJL

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

    あなたへのおすすめ