{"record":{"id":"ec42d8a449818641","repo":"invoke-ai/InvokeAI","slug":"pid-checkpoint-has-len-not-strings-keys-that-ar","errorCode":null,"errorMessage":"PiD checkpoint has {len(not_strings)} keys that are not strings and so cannot name a PidNet parameter: {not_strings[:5]}(+ {len(not_strings) - 5} more)","messagePattern":"PiD checkpoint has (.+?) keys that are not strings and so cannot name a PidNet parameter: (.+?)\\(\\+ (.+?) more\\)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/pid/decode.py","lineNumber":265,"sourceCode":"\n\ndef load_pid_decoder(state_dict: dict[Any, Tensor], backbone: BaseModelType) -> PidNet:\n    \"\"\"Instantiate a PidNet for *backbone* and populate it with *state_dict*.\n\n    The state dict is expected to be the model-manager loader's output, i.e.\n    already stripped of the `net.` prefix used by NVIDIA's distill model\n    serialisation. The caller still owns dtype/device placement of the\n    returned net.\n    \"\"\"\n    net = build_pid_net(backbone)\n\n    # 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 = (","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/pid/decode.py#L247-L283","documentation":"Before handing the state dict to torch, load_pid_decoder checks that every checkpoint key is a string, because nn.Module.load_state_dict would otherwise crash with an opaque AttributeError inside .startswith(). Non-string keys mean the .pth file unpickled to something malformed (e.g. a dict keyed by ints/objects), so it is rejected up front with a RuntimeError showing up to 5 offending keys. This makes a corrupt checkpoint fail with the same readable message style as other unusable checkpoints.","triggerScenarios":"Loading a PiD decoder checkpoint whose top-level state-dict keys are not all strings — e.g. a pickled dict with integer/None/object keys, or a non-state-dict object that happens to be dict-like.","commonSituations":"Corrupted or hand-crafted .pth files, checkpoints saved from non-PyTorch serialization, or loading the wrong file entirely (a pickled dataset/dict instead of a model).","solutions":["Verify the file is a genuine PyTorch state dict (torch.load and inspect type(key) for key in sd)","Re-export/re-save the checkpoint with torch.save(model.state_dict(), path)","Ensure load_pid_decoder is pointed at the intended .pth file"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"sd = torch.load(path, map_location=\"cpu\")\nbad = [k for k in sd if not isinstance(k, str)]\nassert not bad, f\"non-string keys: {bad[:5]}\"","typeGuard":"def is_valid_state_dict(sd) -> bool:\n    return isinstance(sd, dict) and all(isinstance(k, str) for k in sd)","tryCatchPattern":"try:\n    net = load_pid_decoder(path, backbone=backbone)\nexcept RuntimeError as e:\n    if \"not strings\" in str(e):\n        logger.error(f\"Malformed checkpoint {path}: {e}\")\n    raise","preventionTips":["Checksum downloads","Sanity-check keys after torch.load","Never hand-edit .pth files"],"tags":["checkpoint","state-dict","runtime-error","corrupt-file"],"backgroundTag":"corrupt-checkpoint-keys","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}