{"record":{"id":"81ea526c8d8d1d56","repo":"github/spec-kit","slug":"integration-manifest-at-path-must-be-a-json-obje","errorCode":null,"errorMessage":"Integration manifest at {path} must be a JSON object, got {type(data).__name__}","messagePattern":"Integration manifest at (.+?) must be a JSON object, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/integrations/manifest.py","lineNumber":483,"sourceCode":"        \"\"\"Load an existing manifest from disk.\n\n        Raises ``FileNotFoundError`` if the manifest does not exist.\n        \"\"\"\n        inst = cls(key, project_root, resolve_project_root=resolve_project_root)\n        path = inst.manifest_path\n        try:\n            data = json.loads(path.read_text(encoding=\"utf-8\"))\n        except UnicodeDecodeError as exc:\n            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\", [])","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/integrations/manifest.py#L465-L501","documentation":"IntegrationManifest.load() parsed the JSON successfully but the top-level value is not an object (e.g. a list or a bare string). The manifest schema requires a JSON object with keys like files, version, installed_at.","triggerScenarios":"Manifest file whose root is [] , \"text\", 42, true, etc. — usually the result of a bad hand edit, a script writing the wrong structure, or a partial rewrite that left only a fragment value.","commonSituations":"Automation jq-transforming the manifest and emitting an array; manual truncation leaving only a value; tools that write JSONL into the file.","solutions":["Inspect the top-level type: python -c \"import json;print(type(json.load(open('<path>'))))\"","Restore the object structure from git history, or delete and regenerate via specify integration install <key>","Fix the script that rewrote the manifest to always emit an object"],"exampleFix":"# before\n[\".claude/commands/build.md\"]\n# after\n{\"files\": {\".claude/commands/build.md\": \"<sha256>\"}, \"integration\": \"claude\", \"version\": \"\"}","handlingStrategy":"type-guard","validationCode":"import json\ndata = json.loads(manifest.manifest_path.read_text(encoding=\"utf-8\"))\nif not isinstance(data, dict):\n    regenerate_manifest(key, root)","typeGuard":"def is_manifest_object(data: object) -> bool:\n    return isinstance(data, dict) and isinstance(data.get(\"files\", {}), dict)","tryCatchPattern":"try:\n    IntegrationManifest.load(key, root)\nexcept ValueError as exc:\n    if \"must be a JSON object\" in str(exc):\n        regenerate_manifest(key, root)\n    else:\n        raise","preventionTips":["Make scripts that transform manifests always emit objects","jq-rewrite with '. as object' semantics, verify output shape","Keep the manifest under git for easy restore"],"tags":["manifest","json","schema"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}