mvanhorn/last30days-skill · error · HandoffContractError
Pending discovery report {path} must carry a top-level \"rep
Error message
Pending discovery report {path} must carry a top-level \"report\" object. {_RESUME_REMEDY} What it means
`_parse_pending_file` raises HandoffContractError when the pending report's envelope is valid but its top-level `report` value is not an object. The `report` key carries the leg-2-authored discovery report that `--discover --finalize` stamps angles onto and emits; without a well-shaped report there is nothing to finalize. The remedy is the resume remedy: re-run leg 2, or restart the protocol if the bundle is also stale. Note `angle_inputs` is parsed leniently (missing/ill-shaped degrades to empty), but `report` is strict.
Source
Thrown at skills/last30days/scripts/lib/discovery_handoff.py:587
f"{_searched_lines(searched)}\n{_RESUME_REMEDY}"
)
return _parse_pending_file(path)
def _parse_pending_file(path: Path) -> PendingReport:
payload, bundle_id, generated_at = _parse_handoff_envelope(
path,
label="Pending discovery report",
kind=schema.DISCOVERY_PENDING_KIND,
schema_version=schema.DISCOVERY_PENDING_SCHEMA_VERSION,
remedy=_RESUME_REMEDY,
missing_id_context="angles cannot bind to it",
stale_context="the judged window it captured has moved on",
)
version = payload.get("schema_version")
report = payload.get("report")
if not isinstance(report, dict):
raise HandoffContractError(
f"Pending discovery report {path} must carry a top-level "
f"\"report\" object. {_RESUME_REMEDY}"
)
# Lenient per row (engine-written, but one corrupt row must not discard
# the rest): keep only well-shaped angle-input entries.
angle_inputs_raw = payload.get("angle_inputs")
angle_inputs = {
str(nomination_id): {
str(key): str(value) for key, value in info.items()
}
for nomination_id, info in (
angle_inputs_raw.items() if isinstance(angle_inputs_raw, dict) else ()
)
if isinstance(info, dict)
}
return PendingReport(
schema_version=str(version),
bundle_id=bundle_id,View on GitHub (pinned to c7460f6114)
Solutions
- Check `jq '.report | type' <path>` — must be 'object'.
- Re-run the resume leg (`--discover --judgments <file>`) to regenerate the pending report at full fidelity; hand-repair of the report object is not supported.
- Keep `--save-dir` consistent across legs 2 and 3 so the correct pending file is found.
Example fix
# before: jq 'del(.report)' accidentally applied to discover-pending.json # after: re-run leg 2 in the same save dir python3 last30days.py "topic" --discover --judgments judgments.json --save-dir /tmp/run
Defensive patterns
Strategy: type-guard
Validate before calling
import json
from pathlib import Path
def pending_has_report_object(path: Path) -> bool:
try:
return isinstance(json.loads(path.read_text(encoding="utf-8")).get("report"), dict)
except (OSError, json.JSONDecodeError):
return False Type guard
def report_is_object(payload: dict) -> bool:
return isinstance(payload.get("report"), dict) Try / catch
from lib import discovery_handoff
try:
pending = discovery_handoff.load_pending_report(save_dir=save_dir, config_dir=config_dir)
except discovery_handoff.HandoffContractError as exc:
if '"report" object' in exc.message:
# strict field: regenerate by re-running leg 2
... Prevention
- Never trim keys out of discover-pending.json to shrink it; regenerate instead.
- Let leg 2 write the file to completion; interrupting it mid-write can damage the report object.
When it happens
Trigger: `discover-pending.json` edited so `report` became an array/string/null (e.g. `jq '.report |= [...]'` or `del(.report)` then re-adding wrong); a partially-written pending file from an interrupted leg 2; hand-construction of a pending report.
Common situations: Users trimming the pending file to 'reduce size'; sync-tool partial uploads; crash mid-write in leg 2; attempting to hand-forge leg-3 input.
Related errors
- {label} {path} must be a top-level JSON object, got {type(pa
- Nominations bundle {path} must carry a top-level "nomination
- {label} {path} is not valid JSON: {exc}
- {label} {path} has kind {file_kind!r}; expected {kind!r}. {r
- {label} {path} is missing its bundle_id; {missing_id_context
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/5ac5efddc46e0473.
Report an issue: GitHub.