affaan-m/ECC · error · ContractError

receipt reference digest has conflicting source durations

Error message

receipt reference digest has conflicting source durations

What it means

When two receipt references cite the same artifact digest (same sha256) but declare different source_duration values, the receipt is self-contradictory: the same file cannot have two durations. validate_bundle builds a digest→duration map and raises this ContractError on the first conflict, protecting downstream recipe validation that relies on consistent durations.

Solutions

  1. Regenerate the receipt with the standard pipeline so duplicates share one consistent duration
  2. Make all references with the same sha256 carry the identical source_duration value
  3. Remove duplicate/stale reference entries for the same digest
  4. Restore the receipt from a single coherent run rather than merging receipts

Example fix

// before: same digest, conflicting durations
[{"sha256": "abc", "source_duration": 12.5}, {"sha256": "abc", "source_duration": 13.0}]
// after
[{"sha256": "abc", "source_duration": 12.5}, {"sha256": "abc", "source_duration": 12.5}]
Defensive patterns

Strategy: validation

Validate before calling

import json
def durations_consistent(receipt: dict) -> bool:
    seen = {}
    for ref in receipt.get("references", []):
        digest, dur = ref.get("sha256"), ref.get("source_duration")
        if digest in seen and seen[digest] != dur:
            return False
        seen[digest] = dur
    return True

Type guard

def no_conflicting_durations(references: list) -> bool:
    seen: dict = {}
    for ref in references:
        d = ref.get("sha256"); dur = ref.get("source_duration")
        if d in seen and seen[d] != dur:
            return False
        seen[d] = dur
    return True

Try / catch

from tasteforge.contract import ContractError
try:
    validate_bundle(out_dir)
except ContractError as e:
    if "conflicting source durations" in str(e):
        regenerate_bundle(out_dir)  # merged/edited receipts are unusable

Prevention

When it happens

Trigger: A receipt whose references list contains the same sha256 twice with differing source_duration floats — e.g. merging receipts from two runs, editing one entry's duration, or a generator emitting duplicate references with stale durations.

Common situations: Manually merging or patching receipts, copy-paste edits to fix durations for one reference but not its duplicates, or custom tooling that re-derives durations per-clip instead of per-source-file.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/4d94f127ad1e38f6. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/contract.py:478

        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)