mvanhorn/last30days-skill · error · HandoffContractError
Nominations bundle {path} contains no readable nominations (
Error message
Nominations bundle {path} contains no readable nominations (leg 1 never writes an empty pool). {_RESWEEP_REMEDY} What it means
`_parse_bundle_file` raises HandoffContractError when the `nominations` array exists but yields zero readable rows — either literally empty or every row was skipped as malformed. The comment explains why this fails closed: leg 1 never writes an empty bundle (a zero-nomination sweep short-circuits without writing a file), so an empty/all-invalid pool is corrupt state, and silently handing the resume leg an empty pool would produce a meaningless run. The remedy is a fresh re-sweep.
Source
Thrown at skills/last30days/scripts/lib/discovery_handoff.py:511
)
if isinstance(metrics, dict)
}
nominations.append(BundleNomination(
nomination_id=str(row.get("id") or f"n{position}"),
nomination=nomination,
cluster_id=str(row.get("cluster_id") or ""),
heuristic_name=str(row.get("heuristic_name") or ""),
heuristic_junk=bool(row.get("heuristic_junk")),
sources=[str(source) for source in row.get("sources") or []],
engagement_by_source=engagement,
))
if not nominations:
# Leg 1 never writes an empty bundle (a zero-nomination sweep
# short-circuits with no bundle file), so an empty or all-invalid
# nominations array is corrupt state: fail closed, never hand the
# resume leg a silently empty pool.
raise HandoffContractError(
f"Nominations bundle {path} contains no readable nominations "
f"(leg 1 never writes an empty pool). {_RESWEEP_REMEDY}"
)
# Sweep status is advisory coverage context: restore it through the same
# deserializer every report uses, but degrade a malformed map to empty
# rather than discarding an otherwise-valid pool.
try:
source_status = schema._source_status_from_dict(payload)
except (AttributeError, KeyError, TypeError, ValueError):
_warn(f"ignoring malformed source_status map in {path.name}")
source_status = {}
return NominationsBundle(
schema_version=str(version),
bundle_id=bundle_id,
generated_at=str(generated_at or ""),
from_date=str(payload.get("from_date") or ""),View on GitHub (pinned to c7460f6114)
Solutions
- Inspect `jq '.nominations | length' <path>` — 0 (or all-malformed rows) triggers this.
- If a filter emptied it, restore from the untouched bundle or re-run `--discover --nominate-only` and filter more conservatively.
- Never strip the pool to zero; if the sweep truly found nothing, the correct state is NO bundle file, which leg 2 reports differently (error 25).
Example fix
# before jq '.nominations |= map(select(.worthiness == "high"))' discover-nominations.json > tmp && mv tmp discover-nominations.json # filtered everything # after: keep the pool intact; express junk exclusions in the judgments file, not by editing the bundle
Defensive patterns
Strategy: validation
Validate before calling
import json
from pathlib import Path
def pool_nonempty(path: Path) -> bool:
try:
rows = json.loads(path.read_text(encoding="utf-8")).get("nominations")
return isinstance(rows, list) and any(isinstance(r, dict) for r in rows)
except (OSError, json.JSONDecodeError):
return False Try / catch
from lib import discovery_handoff
try:
bundle = discovery_handoff.load_nominations_bundle(save_dir=save_dir, config_dir=config_dir)
except discovery_handoff.HandoffContractError as exc:
if "no readable nominations" in exc.message:
# emptied pool: fail-closed by design; re-run the sweep
... Prevention
- Any jq filter over nominations must be checked with `jq '.nominations | length'` afterwards to confirm it kept rows.
- A sweep with zero findings writes NO file — don't manufacture an empty one.
When it happens
Trigger: `nominations: []` from a hand edit or bad jq filter that matched nothing (`map(select(...))` filtering everything out); every row individually non-dict (e.g. the array was replaced by strings), leaving zero after lenient skipping.
Common situations: A judgment-authoring step pre-filtering junk nominations too aggressively and emptying the pool; corruption that kept the array wrapper but destroyed rows; attempting to craft a minimal test bundle by hand.
Related errors
- {label} {path} is not valid JSON: {exc}
- {label} {path} must be a top-level JSON object, got {type(pa
- Nominations bundle {path} must carry a top-level "nomination
- Pending discovery report {path} must carry a top-level \"rep
- {label.capitalize()} file {file_path} is not valid JSON: {ex
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/26c0b13539890c57.
Report an issue: GitHub.