{"record":{"id":"98f1be92df050cee","repo":"github/spec-kit","slug":"integration-manifest-files-at-path-must-be-a-m","errorCode":null,"errorMessage":"Integration manifest 'files' at {path} must be a mapping of string paths to string hashes","messagePattern":"Integration manifest 'files' at (.+?) must be a mapping of string paths to string hashes","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/integrations/manifest.py","lineNumber":492,"sourceCode":"            raise ValueError(\n                f\"Integration manifest at {path} is not valid UTF-8\"\n            ) from exc\n        except json.JSONDecodeError as exc:\n            raise ValueError(\n                f\"Integration manifest at {path} contains invalid JSON\"\n            ) from exc\n\n        if not isinstance(data, dict):\n            raise ValueError(\n                f\"Integration manifest at {path} must be a JSON object, \"\n                f\"got {type(data).__name__}\"\n            )\n\n        files = data.get(\"files\", {})\n        if not isinstance(files, dict) or not all(\n            isinstance(k, str) and isinstance(v, str) for k, v in files.items()\n        ):\n            raise ValueError(\n                f\"Integration manifest 'files' at {path} must be a \"\n                \"mapping of string paths to string hashes\"\n            )\n\n        inst.version = data.get(\"version\", \"\")\n        inst._installed_at = data.get(\"installed_at\", \"\")\n        inst._files = files\n\n        recovered = data.get(\"recovered_files\", [])\n        if not isinstance(recovered, list) or not all(\n            isinstance(p, str) for p in recovered\n        ):\n            raise ValueError(\n                f\"Integration manifest 'recovered_files' at {path} must be a \"\n                \"list of string paths\"\n            )\n        inst._recovered_files = set(recovered)\n        # Drop any recovered_files entries that don't correspond to tracked","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/integrations/manifest.py#L474-L510","documentation":"IntegrationManifest.load() found the 'files' entry is missing-wrong-type or its items are not string->string pairs. The files map is path (string) to SHA-256 hash (string); any non-string key or value — including a nested object or numeric hash — is rejected.","triggerScenarios":"A manifest where \"files\" is a list, null, or a dict containing e.g. a list of hashes, an int hash, or a nested mapping per file; typically from hand edits or a schema drift between CLI versions/tools.","commonSituations":"Users changing a hash value to null to 'untrack' a file; third-party tools rewriting hashes as numbers; older/custom generators emitting a different shape.","solutions":["python -m json.tool the file and check every files entry is \"<posix rel path>\": \"<64-hex sha256>\"","Restore from git or delete + re-run specify integration install <key>","Do not hand-edit hashes; use record_file/record_existing via the CLI instead"],"exampleFix":"# before\n\"files\": {\"a.md\": 12345678}\n# after\n\"files\": {\"a.md\": \"1234567890abcdef...\"}","handlingStrategy":"validation","validationCode":"files = data.get(\"files\", {})\nok = isinstance(files, dict) and all(\n    isinstance(k, str) and isinstance(v, str) for k, v in files.items()\n)\nif not ok:\n    regenerate_manifest(key, root)","typeGuard":"def is_valid_files_map(files: object) -> bool:\n    return isinstance(files, dict) and all(\n        isinstance(k, str) and isinstance(v, str) for k, v in files.items()\n    )","tryCatchPattern":"try:\n    IntegrationManifest.load(key, root)\nexcept ValueError as exc:\n    if \"must be a mapping of string paths\" in str(exc):\n        regenerate_manifest(key, root)\n    else:\n        raise","preventionTips":["Do not hand-edit hashes; use the CLI to re-record files","Keep hashes as 64-char hex strings","Validate the shape after any scripted transformation"],"tags":["manifest","json","schema","validation"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}