affaan-m/ECC · error · ContractError
missing receipt
Error message
missing receipt
What it means
validate_bundle requires a receipt.json in every output bundle; this receipt records dry-run status, provider execution, and provider call counts. When the file does not exist, the validator raises this ContractError. The receipt is the audit artifact proving the run stayed offline/dry-run, so its absence invalidates the bundle.
Solutions
- Re-run the full pipeline so receipt.json is regenerated into out_dir
- Verify validate_bundle receives the bundle root (which must contain receipt.json)
- Restore receipt.json from a backup or archive if the bundle was copied incompletely
- If implementing a custom generator, always write receipt.json before invoking the validator
Example fix
# before validate_bundle(out_dir / "manifests") # after assert (out_dir / "receipt.json").is_file() validate_bundle(out_dir)
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def ensure_receipt(out_dir: Path) -> None:
if not (out_dir / "receipt.json").is_file():
raise FileNotFoundError(f"{out_dir} has no receipt.json") Type guard
def has_receipt(out_dir) -> bool:
return (out_dir / "receipt.json").is_file() Try / catch
from tasteforge.contract import ContractError
try:
validate_bundle(out_dir)
except ContractError as e:
if "missing receipt" in str(e):
regenerate_bundle(out_dir) Prevention
- Include receipt.json in archives/backups of bundles
- Regenerate rather than reconstruct receipts manually
- Confirm you pass the bundle root, not a subdirectory, to validate_bundle
When it happens
Trigger: Calling validate_bundle (or _bundle) on an out_dir where receipt.json was never written or was deleted — e.g. interrupted run, wrong directory target, or a generator that omitted receipt emission.
Common situations: Copying partial bundles to another machine, running the validator against a stale checkout that predates receipt emission, or accidentally validating a subdirectory instead of the bundle root.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- missing provenance
- receipt reference cannot bind recipe evidence
- anchor evidence source duration is not bound to its receipt…
- artifact path must be a non-empty relative path
- artifact byte size does not match receipt
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/66ebf30a2f214669.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:460
def validate_bundle(out_dir: str | Path) -> None:
"""Validate required multimodal files and cross-artifact invariants."""
out_dir = Path(out_dir)
_validate_output_tree(out_dir)
specs = [json.loads(path.read_text(encoding="utf-8"))
for path in sorted((out_dir / "genres").glob("*.json"))]
validate_genre_specs(specs)
validate_manifests(out_dir / "manifests")
provenance_path = out_dir / "provenance.json"
if not provenance_path.is_file():
raise ContractError("missing provenance")
validate_provenance(json.loads(provenance_path.read_text(encoding="utf-8")))
receipt_path = out_dir / "receipt.json"
if not receipt_path.is_file():
raise ContractError("missing receipt")
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
if (receipt.get("dry_run") is not True
or receipt.get("provider_execution") is not False
or type(receipt.get("provider_calls")) is not int
or receipt.get("provider_calls") != 0):
raise ContractError("receipt crosses the dry-run boundary")
validate_artifact_receipt(out_dir, receipt)
reference_durations: dict[str, float] = {}
for reference in receipt.get("references", []):
digest = reference.get("sha256")
duration = reference.get("source_duration")
if not isinstance(digest, str) or not _is_finite_real(duration):
raise ContractError("receipt reference cannot bind recipe evidence")
duration = float(duration)
previous = reference_durations.get(digest)
if previous is not None and previous != duration:
raise ContractError("receipt reference digest has conflicting source durations")View on GitHub (pinned to 8321021c54)