invoke-ai/InvokeAI · error · InvalidMatchError

PiD checkpoint has {len(unexpected)} keys PidNet does not ex

Error message

PiD checkpoint has {len(unexpected)} keys PidNet does not expect, which `load_pid_decoder` rejects too: {unexpected[:5]}{_and_more(unexpected)}

What it means

The state dict contains keys PidNet does not define. `load_pid_decoder` would reject them with strict loading, so identification raises `InvalidMatchError` preemptively, listing the first 5 unexpected keys so installation and loading accept exactly the same set of files.

Source

Thrown at invokeai/backend/model_manager/configs/pid_decoder.py:163

    parameter whose shape legitimately varies by backbone, and its variable dimensions each have a
    dedicated check above with a dedicated message.
    """
    # No "this is a base PixDiT_T2I checkpoint" special case, unlike `load_pid_decoder`: those weights
    # carry no `lq_proj` key at all, so such a file never reaches here — `_looks_like_pid_decoder`
    # has already turned it away, and with a better message.
    # Both sorts take `key=str`: a bare checkpoint's keys need not all be strings (see
    # `strip_net_prefix`), and sorting a mixed set raises TypeError — which the factory answers with
    # the `Unknown_Config` registration these checks exist to prevent, so the crash fails as a silent
    # accept rather than loudly. Only `unexpected` can hold one today; sorting both the same way keeps
    # that from depending on which set is on which side of the subtraction.
    if missing := sorted(contract.keys() - shapes.keys(), key=str):
        raise InvalidMatchError(
            f"PiD checkpoint is missing {len(missing)} of the weights required by PidNet; the file is "
            f"incomplete and cannot be used as a PiD decoder: {missing[:5]}{_and_more(missing)}"
        )

    if unexpected := sorted(shapes.keys() - contract.keys(), key=str):
        raise InvalidMatchError(
            f"PiD checkpoint has {len(unexpected)} keys PidNet does not expect, which `load_pid_decoder` "
            f"rejects too: {unexpected[:5]}{_and_more(unexpected)}"
        )

    mismatched = [(k, shapes[k], want) for k, want in contract.items() if k != _LATENT_PROJ_KEY and shapes[k] != want]
    if mismatched:
        k, got, want = mismatched[0]
        raise InvalidMatchError(
            f"PiD checkpoint has {len(mismatched)} weights whose shape PidNet cannot accept "
            f"(e.g. {k}: {got}, expected {want}); loading it would fail with a size mismatch"
        )


def _name_components(mod: ModelOnDisk, override_fields: dict[str, Any]) -> tuple[str, ...]:
    """The name evidence for backbone and variant, most specific first.

    NVIDIA distributes PiD checkpoints as
    ``PiD_res2k_sr4x_official_<backbone>_distill_4step/model_ema_bf16.pth``, so the backbone and the

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Use the official released inference checkpoint from nvidia/PiD rather than a raw training dump
  2. Strip the extra keys (keeping only those matching the PidNet contract) if you must preprocess, then reinstall
  3. If the extra keys are v1.5 modules, see error on unsupported architecture — you need the legacy checkpoint
  4. Upgrade InvokeAI in case support for a newer layout was added

Example fix

// before: checkpoint contains net.lq_proj.* plus net.pit_inject.* (v1.5 modules)
// after: use the legacy release, or strip unexpected keys
sd = {k: v for k, v in torch.load(p).items() if k in required_keys}
Defensive patterns

Strategy: validation

Validate before calling

from invokeai.backend.pid.decode import required_pid_net_shapes
from invokeai.backend.pid.state_dict_utils import pid_net_shapes
import torch
sd = torch.load(ckpt_path, map_location='cpu')
unexpected = pid_net_shapes(sd).keys() - required_pid_net_shapes().keys()
if unexpected:
    raise SystemExit(f'Extra keys PidNet rejects: {sorted(unexpected, key=str)[:5]}')

Type guard

def keys_match_pid_net(sd: dict) -> bool:
    from invokeai.backend.pid.decode import required_pid_net_shapes
    from invokeai.backend.pid.state_dict_utils import pid_net_shapes
    shapes = pid_net_shapes(sd)
    contract = required_pid_net_shapes()
    return shapes.keys() == contract.keys()

Try / catch

try:
    install_model(path)
except InvalidMatchError as e:
    if 'does not expect' in str(e):
        logger.error('Checkpoint has extra keys (training dump or newer arch): %s', e)
    else:
        raise

Prevention

When it happens

Trigger: `_raise_if_pid_net_contract_unmet` in `from_model_on_disk` finds `shapes.keys() - contract.keys()` non-empty — extra tensors in the .pth (e.g. EMA wrappers, optimizer state, v1.5-only modules like PiT injection in an otherwise legacy file).

Common situations: Full training dumps (state_dict plus optimizer/scheduler entries) instead of the released inference checkpoint; new-architecture weights loaded by an older InvokeAI; renamed keys after manual surgery.

Related errors


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