{"record":{"id":"4f38586c076cc3dd","repo":"invoke-ai/InvokeAI","slug":"pid-checkpoint-has-unexpected-keys-not-present-in","errorCode":null,"errorMessage":"PiD checkpoint has unexpected keys not present in PidNet: {unexpected[:5]}","messagePattern":"PiD checkpoint has unexpected keys not present in PidNet: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/pid/decode.py","lineNumber":277,"sourceCode":"    # A `.pth` unpickles to whatever it contains, and a bare (un-prefixed) checkpoint reaches here\n    # with its keys untouched — see `strip_net_prefix`. `nn.Module.load_state_dict` calls\n    # `.startswith()` on every key, so a non-string one raises AttributeError from inside torch\n    # before any of the reporting below runs. Reject it here instead, so a malformed checkpoint gets\n    # the same kind of message as every other unusable one.\n    if not_strings := sorted((k for k in state_dict if not isinstance(k, str)), key=str):\n        raise RuntimeError(\n            f\"PiD checkpoint has {len(not_strings)} keys that are not strings and so cannot name a \"\n            f\"PidNet parameter: {not_strings[:5]}\"\n            + (f\" (+ {len(not_strings) - 5} more)\" if len(not_strings) > 5 else \"\")\n        )\n\n    # strict=False so we can report missing and unexpected keys separately; both are fatal. The model\n    # cache builds loaders under `skip_torch_weight_init()`, which no-ops every `reset_parameters()`,\n    # so a key the checkpoint does not supply is left as uninitialised memory rather than a sane\n    # default — a partial checkpoint would decode to garbage / NaNs instead of failing.\n    missing, unexpected = net.load_state_dict(state_dict, strict=False)\n    if unexpected:\n        raise RuntimeError(\n            f\"PiD checkpoint has unexpected keys not present in PidNet: {unexpected[:5]}\"\n            + (f\" (+ {len(unexpected) - 5} more)\" if len(unexpected) > 5 else \"\")\n        )\n    if missing:\n        lq = [k for k in missing if k.startswith(\"lq_proj.\")]\n        detail = (\n            \" (the LQ projection is incomplete — this looks like a base PixDiT_T2I checkpoint rather than a \"\n            \"PiD super-resolution decoder)\"\n            if lq and len(lq) == len(missing)\n            else \"\"\n        )\n        raise RuntimeError(\n            f\"PiD checkpoint is missing {len(missing)} keys required by PidNet{detail}: {missing[:5]}\"\n            + (f\" (+ {len(missing) - 5} more)\" if len(missing) > 5 else \"\")\n        )\n    return net\n\n","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/pid/decode.py#L259-L295","documentation":"load_pid_decoder loads with strict=False so missing and unexpected keys can be reported separately; both are fatal. If the checkpoint contains keys that do not exist in the constructed PidNet (architecture mismatch, wrong backbone, extra prefixes, or a foreign checkpoint), it raises RuntimeError listing up to 5 unexpected keys. Loading is refused because silently ignoring extra keys usually means the rest of the weights do not correspond to this architecture either.","triggerScenarios":"Calling load_pid_decoder with a checkpoint whose keys don't match PidNet: wrong backbone argument, a checkpoint from a different architecture (e.g. base PixDiT_T2I with extra modules), or keys saved under a different naming scheme.","commonSituations":"Pointing a PiD decoder loader at an unrelated diffusion checkpoint, renaming modules between library versions, or mixing checkpoints across model variants.","solutions":["Verify the checkpoint actually belongs to the PiD decoder for the given backbone","Re-run strip_net_prefix / strip any 'module.' or wrapper prefixes from the checkpoint keys before loading","Match the backbone argument to the checkpoint's true architecture","Regenerate/download the correct checkpoint"],"exampleFix":"// before\nnet = load_pid_decoder(ckpt, backbone=\"pid_xl\")  # ckpt is a base PixDiT_T2I\n// after\nnet = load_pid_decoder(ckpt_pid_decoder, backbone=\"pid_xl\")","handlingStrategy":"validation","validationCode":"sd = torch.load(path, map_location=\"cpu\")\nextra = set(sd) - set(build_pid_net(backbone).state_dict())\nassert not extra, f\"unexpected keys: {sorted(extra)[:5]}\"","typeGuard":null,"tryCatchPattern":"try:\n    net = load_pid_decoder(path, backbone=backbone)\nexcept RuntimeError as e:\n    if \"unexpected keys\" in str(e):\n        logger.error(f\"Checkpoint mismatch: {e}\")\n    raise","preventionTips":["Store backbone name in checkpoint metadata","Strip wrapper prefixes consistently","Round-trip test save/load in CI"],"tags":["checkpoint","state-dict","key-mismatch","model-loading"],"backgroundTag":"state-dict-key-mismatch","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}