mvanhorn/last30days-skill · error · HandoffContractError
The {label} file is bound to bundle_id {file_bundle_id!r} bu
Error message
The {label} file is bound to bundle_id {file_bundle_id!r} but the {noun} is {bundle.bundle_id!r}. {location_label}:\n{_searched_lines(searched)}\nCorrect the bundle_id field in your {label} file to {bundle.bundle_id!r} and re-run this same leg. What it means
Raised by _require_bundle_binding when a host file (judgments/angles) carries a bundle_id that does not match the bundle_id of the nominations bundle or pending report the engine is currently operating on. The handoff protocol binds every host file to one bundle so stale judgments from a previous run are never silently applied to a new nomination set. The message includes every bundle/pending-report path searched so the operator can find the current ID.
Source
Thrown at skills/last30days/scripts/lib/discovery_handoff.py:667
pending report on the finalize leg - so a host's retry is not misdirected
at the nominations bundle. A mismatch means the host echoed the wrong id
into an otherwise-current file, so the remedy is the cheap one - correct
the bundle_id field and re-run this same leg - never the expensive
re-sweep/resume remedies (those belong to missing/stale state)."""
file_bundle_id = str(payload.get("bundle_id") or "")
if file_bundle_id == bundle.bundle_id:
return
if isinstance(bundle, PendingReport):
searched = _search_paths(save_dir, config_dir, pending_report_path)
noun = "current pending discovery report"
location_label = "Pending-report locations searched"
else:
searched = _search_paths(save_dir, config_dir, nominations_bundle_path)
noun = "current nominations bundle"
location_label = "Bundle locations searched"
if not searched and bundle.path is not None:
searched = [bundle.path]
raise HandoffContractError(
f"The {label} file is bound to bundle_id {file_bundle_id!r} but the "
f"{noun} is {bundle.bundle_id!r}. {location_label}:\n"
f"{_searched_lines(searched)}\n"
f"Correct the bundle_id field in your {label} file to "
f"{bundle.bundle_id!r} and re-run this same leg."
)
def _truncate_at_word(text: str, max_chars: int) -> str:
"""Cap ``text`` at ``max_chars``, cutting back to a word boundary and
stripping trailing punctuation. Text within the cap passes through
untouched."""
if len(text) <= max_chars:
return text
return text[:max_chars].rsplit(" ", 1)[0].rstrip(_TRUNCATE_STRIP_CHARS)
def _sanitized_name(raw: object) -> str | None:View on GitHub (pinned to c7460f6114)
Solutions
- Read the current bundle_id from the bundle/pending-report path listed in the error's searched-locations lines and set the bundle_id field in your host file to exactly that value, then re-run the same leg.
- If the stale file is from an abandoned run, regenerate the judgments/angles file against the current bundle instead of editing the old one.
- If you expected the old bundle to still be current, check that you are running with the same --save-dir / config dir as the run that produced it.
Example fix
// before (judgments.json)
{
"bundle_id": "3f0c9a...-old-run",
"judgments": []
}
// after
{
"bundle_id": "<bundle_id printed by the error / found in the searched bundle path>",
"judgments": []
} Defensive patterns
Strategy: validation
Validate before calling
import json
def bundle_id_matches(path: str, bundle) -> bool:
payload = json.loads(open(path, encoding="utf-8").read())
return payload.get("bundle_id") == bundle.bundle_id Try / catch
try:
load_judgments(path, bundle, ...)
except HandoffContractError as e:
# the message lists every searched bundle path; read the current id from there
current = extract_bundle_id_from_error(e) # then rewrite the host file and re-run Prevention
- Never hand-copy bundle_id between runs; read it from the bundle/pending-report file the engine just wrote.
- Treat a fresh nominations bundle as invalidating prior judgments/angles files.
- Automate: after each leg, script the bundle_id from the bundle file into the host file template.
When it happens
Trigger: Re-running a discovery leg after the engine regenerated the nominations bundle (new bundle_id) while reusing a judgments/angles file written for the old run; hand-copying an example judgments file with a placeholder bundle_id; two concurrent runs sharing a save_dir with different bundles.
Common situations: Iterating on judgments across legs: the user edits the file, re-runs, and the engine has already produced a fresh pending report with a different ID; using a file from a different machine or save_dir; editing bundle_id by hand and introducing a typo or keeping quotes from the error message (the repr includes quotes — paste the inner value only).
Related errors
- {label.capitalize()} file {file_path} must be a top-level JS
- Judgments file {path} must carry a top-level \"judgments\" l
- Angles file {path} must carry a top-level \"angles\" list.
- Unknown search source in {flag_name}: {source}
- {flag_name} requires at least one source.
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/c66650540e0590eb.
Report an issue: GitHub.