affaan-m/ECC · error · ContractError
receipt reference cannot bind recipe evidence
Error message
receipt reference cannot bind recipe evidence
What it means
Each receipt reference must bind recipe evidence: a sha256 digest (string) and a finite numeric source_duration. If a reference lacks a valid string digest or its source_duration is missing/non-numeric/NaN/inf, the reference cannot be tied to recipe evidence and this ContractError is raised. This ensures every cited artifact has measurable, finite provenance.
Solutions
- Regenerate the receipt through the normal tasteforge pipeline so all references carry sha256 and numeric source_duration
- Ensure each reference has sha256 as a string and source_duration as a finite number (not string/null/NaN/Infinity)
- Fix custom generators to validate reference entries before writing receipt.json
- Restore receipt.json from a known-good run
Example fix
// before
{"references": [{"sha256": null, "source_duration": "12.5"}]}
// after
{"references": [{"sha256": "abc123...", "source_duration": 12.5}]} Defensive patterns
Strategy: validation
Validate before calling
import math, json
def references_valid(receipt: dict) -> bool:
for ref in receipt.get("references", []):
if not isinstance(ref.get("sha256"), str):
return False
d = ref.get("source_duration")
if not isinstance(d, (int, float)) or isinstance(d, bool) or not math.isfinite(d):
return False
return True Type guard
def is_valid_reference(ref) -> bool:
import math
d = ref.get("source_duration")
return (isinstance(ref.get("sha256"), str)
and isinstance(d, (int, float)) and not isinstance(d, bool)
and math.isfinite(d)) Try / catch
from tasteforge.contract import ContractError
try:
validate_bundle(out_dir)
except ContractError as e:
if "cannot bind recipe evidence" in str(e):
fix_or_regenerate_receipt(out_dir) Prevention
- Generate receipts via the pipeline, never by hand
- Emit durations as JSON numbers, not strings
- Sanity-check receipts with a script before validation
When it happens
Trigger: A receipt references array entry missing "sha256" or "source_duration", a null duration, a string duration like "12.5", or NaN/Infinity values — typically from hand-written or hand-edited receipts.
Common situations: Writing receipts by hand or with a custom generator, truncating JSON during copy, or emitting durations as strings from a templating step.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- missing receipt
- 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
- artifact has invalid modality binding
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/776d5b36cb6fd9ae.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:474
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")
reference_durations[digest] = duration
recipe_path = out_dir / "resolve" / "effect_recipe.json"
if not recipe_path.is_file():
raise ContractError("missing Resolve effect recipe")
validate_effect_recipe(
json.loads(recipe_path.read_text(encoding="utf-8")),
reference_durations=reference_durations,
)
View on GitHub (pinned to 8321021c54)