{"record":{"id":"c378f8524ec3b701","repo":"invoke-ai/InvokeAI","slug":"pid-checkpoint-has-len-unexpected-keys-pidnet-d","errorCode":null,"errorMessage":"PiD checkpoint has {len(unexpected)} keys PidNet does not expect, which `load_pid_decoder` rejects too: {unexpected[:5]}{_and_more(unexpected)}","messagePattern":"PiD checkpoint has (.+?) keys PidNet does not expect, which `load_pid_decoder` rejects too: (.+?)(.+?)","errorType":"exception","errorClass":"InvalidMatchError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/model_manager/configs/pid_decoder.py","lineNumber":163,"sourceCode":"    parameter whose shape legitimately varies by backbone, and its variable dimensions each have a\n    dedicated check above with a dedicated message.\n    \"\"\"\n    # No \"this is a base PixDiT_T2I checkpoint\" special case, unlike `load_pid_decoder`: those weights\n    # carry no `lq_proj` key at all, so such a file never reaches here — `_looks_like_pid_decoder`\n    # has already turned it away, and with a better message.\n    # Both sorts take `key=str`: a bare checkpoint's keys need not all be strings (see\n    # `strip_net_prefix`), and sorting a mixed set raises TypeError — which the factory answers with\n    # the `Unknown_Config` registration these checks exist to prevent, so the crash fails as a silent\n    # accept rather than loudly. Only `unexpected` can hold one today; sorting both the same way keeps\n    # that from depending on which set is on which side of the subtraction.\n    if missing := sorted(contract.keys() - shapes.keys(), key=str):\n        raise InvalidMatchError(\n            f\"PiD checkpoint is missing {len(missing)} of the weights required by PidNet; the file is \"\n            f\"incomplete and cannot be used as a PiD decoder: {missing[:5]}{_and_more(missing)}\"\n        )\n\n    if unexpected := sorted(shapes.keys() - contract.keys(), key=str):\n        raise InvalidMatchError(\n            f\"PiD checkpoint has {len(unexpected)} keys PidNet does not expect, which `load_pid_decoder` \"\n            f\"rejects too: {unexpected[:5]}{_and_more(unexpected)}\"\n        )\n\n    mismatched = [(k, shapes[k], want) for k, want in contract.items() if k != _LATENT_PROJ_KEY and shapes[k] != want]\n    if mismatched:\n        k, got, want = mismatched[0]\n        raise InvalidMatchError(\n            f\"PiD checkpoint has {len(mismatched)} weights whose shape PidNet cannot accept \"\n            f\"(e.g. {k}: {got}, expected {want}); loading it would fail with a size mismatch\"\n        )\n\n\ndef _name_components(mod: ModelOnDisk, override_fields: dict[str, Any]) -> tuple[str, ...]:\n    \"\"\"The name evidence for backbone and variant, most specific first.\n\n    NVIDIA distributes PiD checkpoints as\n    ``PiD_res2k_sr4x_official_<backbone>_distill_4step/model_ema_bf16.pth``, so the backbone and the","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/model_manager/configs/pid_decoder.py#L145-L181","documentation":"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.","triggerScenarios":"`_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).","commonSituations":"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.","solutions":["Use the official released inference checkpoint from nvidia/PiD rather than a raw training dump","Strip the extra keys (keeping only those matching the PidNet contract) if you must preprocess, then reinstall","If the extra keys are v1.5 modules, see error on unsupported architecture — you need the legacy checkpoint","Upgrade InvokeAI in case support for a newer layout was added"],"exampleFix":"// before: checkpoint contains net.lq_proj.* plus net.pit_inject.* (v1.5 modules)\n// after: use the legacy release, or strip unexpected keys\nsd = {k: v for k, v in torch.load(p).items() if k in required_keys}","handlingStrategy":"validation","validationCode":"from invokeai.backend.pid.decode import required_pid_net_shapes\nfrom invokeai.backend.pid.state_dict_utils import pid_net_shapes\nimport torch\nsd = torch.load(ckpt_path, map_location='cpu')\nunexpected = pid_net_shapes(sd).keys() - required_pid_net_shapes().keys()\nif unexpected:\n    raise SystemExit(f'Extra keys PidNet rejects: {sorted(unexpected, key=str)[:5]}')","typeGuard":"def keys_match_pid_net(sd: dict) -> bool:\n    from invokeai.backend.pid.decode import required_pid_net_shapes\n    from invokeai.backend.pid.state_dict_utils import pid_net_shapes\n    shapes = pid_net_shapes(sd)\n    contract = required_pid_net_shapes()\n    return shapes.keys() == contract.keys()","tryCatchPattern":"try:\n    install_model(path)\nexcept InvalidMatchError as e:\n    if 'does not expect' in str(e):\n        logger.error('Checkpoint has extra keys (training dump or newer arch): %s', e)\n    else:\n        raise","preventionTips":["Use released inference checkpoints, not raw training dumps with optimizer state","Match checkpoint version to the InvokeAI-supported legacy architecture","Don't hand-edit/rename checkpoint keys"],"tags":["checkpoint","unexpected-keys","strict-loading"],"backgroundTag":"checkpoint-unexpected-keys","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}