invoke-ai/InvokeAI · error · ValueError

Krea-2 LoRA has conflicting layers that normalize to the sam

Error message

Krea-2 LoRA has conflicting layers that normalize to the same target '{converted_key}' (from '{source_keys[converted_key]}' and '{key}'). This mixed layout is unsupported - refusing to silently drop one of the layers.

What it means

Raised while converting a Kohya-layout Krea-2 LoRA state dict. After mapping kohya keys to diffusers-style keys, two source keys normalize to the same converted target key, which would silently overwrite one layer. The loader raises instead of dropping weights.

Source

Thrown at invokeai/backend/patches/lora_conversions/krea2_lora_conversion_utils.py:232

    converted_state_dict: Dict[str, torch.Tensor] = {}
    source_keys: dict[str, str] = {}
    for key, value in state_dict.items():
        converted_key = key
        if isinstance(key, str) and key.startswith(_KREA2_KOHYA_PREFIX):
            # The flattened module path runs up to the first '.'; the weight suffix (``lora_down.weight``,
            # ``alpha``, ...) follows it. Some writers emit a doubled separator after the prefix.
            flat_path, dot, weight_suffix = key[len(_KREA2_KOHYA_PREFIX) :].lstrip("_").partition(".")
            module_path = _unflatten_kohya_krea2_module_path(flat_path)
            # Only rewrite when ``_group_by_layer`` can split the suffix back off. Un-flattening introduces
            # dots into the module path, and the grouper's fallback for an unknown suffix is a blind
            # ``rsplit(".", 2)`` — on a dotted path that cuts *inside the module name*, fusing two modules
            # into one bogus layer that aborts the whole load. LyCORIS suffixes such as ``.lokr_w1`` or
            # ``.hada_w1_a`` hit exactly that. Flattened, they have no interior dot and group harmlessly,
            # so leaving them verbatim keeps them at the pre-existing warn-and-skip behaviour.
            if module_path is not None and flat_path in fully_convertible_flat_paths:
                converted_key = f"{module_path}{dot}{weight_suffix}"
        if converted_key in converted_state_dict:
            raise ValueError(
                f"Krea-2 LoRA has conflicting layers that normalize to the same target '{converted_key}' "
                f"(from '{source_keys[converted_key]}' and '{key}'). This mixed layout is unsupported - "
                "refusing to silently drop one of the layers."
            )
        converted_state_dict[converted_key] = value
        source_keys[converted_key] = str(key)
    return converted_state_dict


def is_state_dict_likely_krea2_lora(state_dict: dict[str | int, torch.Tensor]) -> bool:
    """Checks if the provided state dict is likely a Krea-2 LoRA.

    Requires the distinctive Krea-2 ``text_fusion`` / ``txtfusion`` / ``time_mod_proj`` modules so it does not
    false-match Qwen-Image or Z-Image LoRAs that also carry ``transformer.transformer_blocks.`` keys.
    """
    str_keys = [k for k in state_dict.keys() if isinstance(k, str)]
    has_krea2_module = any(any(sig in k for sig in KREA2_TRANSFORMER_SIGNATURE_KEYS) for k in str_keys)
    has_lora_suffix = any(

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Inspect the file's keys and delete the duplicate entries so only one key normalizes to each target.
  2. Regenerate the LoRA export with a single layout (plain Kohya LoRA without LyCORIS-style extra tensors for the same modules).
  3. Split the file: load the LyCORIS part with a LyCORIS-aware loader and the plain LoRA part here.

Example fix

// before (colliding keys in one file)
'lora_unet_blocks_0_attn_qkv.alpha'
'lora_unet_blocks_0_attn_qkv.lokr_w1'
// after: one file per format, single weight key per module
'lora_unet_blocks_0_attn_qkv.lora_down'
Defensive patterns

Strategy: validation

Validate before calling

seen = set()
for key in state_dict:
    # apply your own normalization matching kohya->diffusers mapping
    norm = key.replace('lora_unet_', '').replace('lora_te_', '')
    if norm in seen:
        raise ValueError(f'kohya keys collide after normalization: {norm}')
    seen.add(norm)

Type guard

def is_pure_kohya_layout(state_dict: dict[str, object]) -> bool:
    return all(isinstance(k, str) and (k.startswith('lora_unet_') or k.startswith('lora_te_')) for k in state_dict)

Try / catch

try:
    model = lora_model_from_krea2_state_dict(state_dict)
except ValueError as e:
    if 'conflicting layers' in str(e) and 'from' in str(e):
        logger.error('Kohya LoRA collision: %s', e)
        # drop the duplicate entry named after 'and' in the message
    else:
        raise

Prevention

When it happens

Trigger: lora_model_from_krea2_state_dict -> _maybe_convert_kohya_krea2_state_dict with a state dict where two kohya keys (after module_path/weight_suffix normalization, e.g. differing only in suffixes like .lokr_w1 vs a standard lora_down weight) collapse to the same converted_key.

Common situations: Kohya-exported files that contain both an alpha-style and a full-weight entry for the same module; LyCORIS files mixed with plain LoRA keys; checkpoints merged from two sources that each define the same layer.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/0a466f29fcb165cf. Report an issue: GitHub.