github/spec-kit · error · ValueError
Integration manifest 'recovered_files' at {path} must be a l
Error message
Integration manifest 'recovered_files' at {path} must be a list of string paths What it means
IntegrationManifest.load() validates the optional 'recovered_files' array: it must be a list of strings. It tracks files re-adopted after recovery, so non-string entries (ints, objects, nulls) indicate corruption or schema misuse.
Source
Thrown at src/specify_cli/integrations/manifest.py:505
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
# files — defensive against externally-edited or partially-corrupted
# manifests. Inconsistent state self-corrects on next save().
inst._recovered_files &= set(inst._files.keys())
stored_key = data.get("integration", "")
if stored_key and stored_key != key:
raise ValueError(
f"Manifest at {path} belongs to integration {stored_key!r}, "
f"not {key!r}"
)
return inst
View on GitHub (pinned to bf88c9f9a8)
Solutions
- Check that recovered_files is [] or a list of path strings matching tracked files
- Safest fix: delete the manifest and re-run specify integration install <key> (load() also self-heals stray entries on next save, but the type error must be fixed first)
- Restore a known-good copy from git
Example fix
# before
"recovered_files": {"a.md": true}
# after
"recovered_files": ["a.md"] Defensive patterns
Strategy: validation
Validate before calling
recovered = data.get("recovered_files", [])
ok = isinstance(recovered, list) and all(isinstance(p, str) for p in recovered)
if not ok:
data["recovered_files"] = [] # field is optional; reset it
rewrite_manifest(data) Type guard
def is_valid_recovered_list(v: object) -> bool:
return isinstance(v, list) and all(isinstance(p, str) for p in v) Try / catch
try:
IntegrationManifest.load(key, root)
except ValueError as exc:
if "recovered_files" in str(exc):
reset_recovered_field_and_reload()
else:
raise Prevention
- Treat recovered_files as CLI-internal; do not edit it
- Reset it to [] rather than inventing shapes
- Re-run install after recovery to let save() normalize state
When it happens
Trigger: A manifest where recovered_files is a dict, a string, or a list containing non-strings — from manual edits, jq scripts, or a different tool version writing an incompatible shape.
Common situations: Hand-merging two manifests; scripts copying recovered_files between files; partial JSON edits.
Related errors
- Integration manifest 'files' at {path} must be a mapping of
- {source} contains an invalid bundle manifest:\n - " + "\n
- Integration manifest at {path} must be a JSON object, got {t
- Aliases for command {cmd_name!r} must be a list
- auth.json must be a JSON object, got {type(raw).__name__}
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/4ebfb3751b2f5085.
Report an issue: GitHub.