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}'. This mixed layout is unsupported - refusing to silently drop one of the layers.

What it means

This error is raised while converting a native Krea-2 LoRA state dict to diffusers key format. Two distinct source keys normalize to the same converted target key, so one would silently overwrite the other and drop weights. The loader refuses the mixed-layout file rather than producing a corrupt model.

Source

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

        key = key.replace(native, diffusers)
    return key


def _maybe_convert_native_krea2_state_dict(
    state_dict: Dict[str, torch.Tensor],
) -> Dict[str, torch.Tensor]:
    """Rewrite native (ComfyUI) Krea-2 LoRA keys to the diffusers layout, leaving diffusers keys untouched."""
    str_keys = [k for k in state_dict.keys() if isinstance(k, str)]
    if not _looks_like_native_krea2_lora(str_keys):
        return state_dict
    converted_state_dict: Dict[str, torch.Tensor] = {}
    for key, value in state_dict.items():
        # `.pt`/`.ckpt` sources can carry non-string keys. They are never native Krea-2 keys, but the
        # substring tests in `_looks_like_native_krea2_key` raise TypeError rather than returning False.
        is_native = isinstance(key, str) and _looks_like_native_krea2_key(key)
        converted_key = _native_krea2_key_to_diffusers(key) if is_native else key
        if converted_key in converted_state_dict:
            raise ValueError(
                f"Krea-2 LoRA has conflicting layers that normalize to the same target '{converted_key}'. "
                "This mixed layout is unsupported - refusing to silently drop one of the layers."
            )
        converted_state_dict[converted_key] = value
    return converted_state_dict


# --- Kohya / LyCORIS (flattened) -> native key mapping ---------------------------------------------------------
# sd-scripts and LyCORIS flatten the module path (``path.replace(".", "_")``) and prefix it with
# ``lora_unet_``, e.g. ``lora_unet_blocks_6_attn_wv.lora_down.weight``. Flattening is lossy — nothing in the key
# records where a '_' used to be a '.' — so we reconstruct the dotted path against the native module vocabulary
# below and accept it only if it lands on a leaf. A key we cannot reconstruct with certainty is left untouched
# rather than rewritten into a plausible-looking key that matches no module.
_KREA2_KOHYA_PREFIX = "lora_unet_"

# Native Krea-2 transformer/text-fusion block leaves. Only the Linears are listed: the non-Linear natives
# (``mod.lin``, ``prenorm``/``postnorm``, ``attn.qknorm.*``, ``last.norm``/``last.modulation``) have no Linear
# counterpart in the diffusers layout — ``mod.lin`` for instance is folded into the ``scale_shift_table``

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Open the LoRA file and remove the duplicate layer(s) so each logical layer appears under exactly one key layout.
  2. Re-export the LoRA from its trainer in a single, consistent key format (all-native Krea-2 or all-diffusers).
  3. If merging two LoRAs, merge their tensors into one key instead of keeping both variants of the same layer.

Example fix

// before: state dict contains both
diffusion_model.blocks.0.attn.qkv.lora_down.weight
transformer.blocks.0.attn.qkv.lora_down.weight
// after: keep exactly one variant per logical layer
diffusion_model.blocks.0.attn.qkv.lora_down.weight
Defensive patterns

Strategy: validation

Validate before calling

from invokeai.backend.patches.lora_conversions.krea2_lora_conversion_utils import _looks_like_native_krea2_key, _native_krea2_key_to_diffusers
seen = set()
for key in state_dict:
    k = _native_krea2_key_to_diffusers(key) if isinstance(key, str) and _looks_like_native_krea2_key(key) else key
    if k in seen:
        raise ValueError(f'duplicate normalized key: {k}')
    seen.add(k)

Type guard

def has_no_krea2_key_collisions(state_dict: dict) -> bool:
    seen = set()
    for key in state_dict:
        k = str(key)
        if k in seen:
            return False
        seen.add(k)
    return True

Try / catch

try:
    model = lora_model_from_krea2_state_dict(state_dict)
except ValueError as e:
    if 'conflicting layers' in str(e):
        logger.error('LoRA file has duplicate layers: %s', e)
        # inspect and deduplicate state_dict keys before retrying
    else:
        raise

Prevention

When it happens

Trigger: Calling lora_model_from_krea2_state_dict on a state dict where, after _native_krea2_key_to_diffusers conversion, two different keys (e.g. a native Krea-2 key and an already-diffusers-format key for the same layer) map to the same converted_key.

Common situations: LoRA files that bundle both native Krea-2 keys and diffusers-style keys for the same module (e.g. assembled from multiple checkpoints or exported by tools that duplicate layers); manually merged state dicts; keys that collide only after prefix stripping/normalization.

Related errors


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