github/spec-kit · error · ValueError

Integration manifest at {path} must be a JSON object, got {t

Error message

Integration manifest at {path} must be a JSON object, got {type(data).__name__}

What it means

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.

Source

Thrown at src/specify_cli/integrations/manifest.py:483

        """Load an existing manifest from disk.

        Raises ``FileNotFoundError`` if the manifest does not exist.
        """
        inst = cls(key, project_root, resolve_project_root=resolve_project_root)
        path = inst.manifest_path
        try:
            data = json.loads(path.read_text(encoding="utf-8"))
        except UnicodeDecodeError as exc:
            raise ValueError(
                f"Integration manifest at {path} is not valid UTF-8"
            ) from exc
        except json.JSONDecodeError as exc:
            raise ValueError(
                f"Integration manifest at {path} contains invalid JSON"
            ) from exc

        if not isinstance(data, dict):
            raise ValueError(
                f"Integration manifest at {path} must be a JSON object, "
                f"got {type(data).__name__}"
            )

        files = data.get("files", {})
        if not isinstance(files, dict) or not all(
            isinstance(k, str) and isinstance(v, str) for k, v in files.items()
        ):
            raise ValueError(
                f"Integration manifest 'files' at {path} must be a "
                "mapping of string paths to string hashes"
            )

        inst.version = data.get("version", "")
        inst._installed_at = data.get("installed_at", "")
        inst._files = files

        recovered = data.get("recovered_files", [])

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Inspect the top-level type: python -c "import json;print(type(json.load(open('<path>'))))"
  2. Restore the object structure from git history, or delete and regenerate via specify integration install <key>
  3. Fix the script that rewrote the manifest to always emit an object

Example fix

# before
[".claude/commands/build.md"]
# after
{"files": {".claude/commands/build.md": "<sha256>"}, "integration": "claude", "version": ""}
Defensive patterns

Strategy: type-guard

Validate before calling

import json
data = json.loads(manifest.manifest_path.read_text(encoding="utf-8"))
if not isinstance(data, dict):
    regenerate_manifest(key, root)

Type guard

def is_manifest_object(data: object) -> bool:
    return isinstance(data, dict) and isinstance(data.get("files", {}), dict)

Try / catch

try:
    IntegrationManifest.load(key, root)
except ValueError as exc:
    if "must be a JSON object" in str(exc):
        regenerate_manifest(key, root)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: Automation jq-transforming the manifest and emitting an array; manual truncation leaving only a value; tools that write JSONL into the file.

Related errors


AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14). Data as JSON: /api/errors/81ea526c8d8d1d56. Report an issue: GitHub.