github/spec-kit · error · ValueError

Integration manifest 'files' at {path} must be a mapping of

Error message

Integration manifest 'files' at {path} must be a mapping of string paths to string hashes

What it means

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.

Source

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

            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", [])
        if not isinstance(recovered, list) or not all(
            isinstance(p, str) for p in recovered
        ):
            raise ValueError(
                f"Integration manifest 'recovered_files' at {path} must be a "
                "list of string paths"
            )
        inst._recovered_files = set(recovered)
        # Drop any recovered_files entries that don't correspond to tracked

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. python -m json.tool the file and check every files entry is "<posix rel path>": "<64-hex sha256>"
  2. Restore from git or delete + re-run specify integration install <key>
  3. Do not hand-edit hashes; use record_file/record_existing via the CLI instead

Example fix

# before
"files": {"a.md": 12345678}
# after
"files": {"a.md": "1234567890abcdef..."}
Defensive patterns

Strategy: validation

Validate before calling

files = data.get("files", {})
ok = isinstance(files, dict) and all(
    isinstance(k, str) and isinstance(v, str) for k, v in files.items()
)
if not ok:
    regenerate_manifest(key, root)

Type guard

def is_valid_files_map(files: object) -> bool:
    return isinstance(files, dict) and all(
        isinstance(k, str) and isinstance(v, str) for k, v in files.items()
    )

Try / catch

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

Prevention

When it happens

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

Common situations: 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.

Related errors


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