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

  1. Regenerate the receipt through the normal tasteforge pipeline so all references carry sha256 and numeric source_duration
  2. Ensure each reference has sha256 as a string and source_duration as a finite number (not string/null/NaN/Infinity)
  3. Fix custom generators to validate reference entries before writing receipt.json
  4. 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

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


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)