affaan-m/ECC · error · ContractError

artifact has an unbound source duration

Error message

artifact {relative} has an unbound source duration

What it means

For a media_seconds source, the receipt must record 'source_duration' equal to the known duration of the cited reference file, as computed from the bundle's source_durations map. This error is raised when the expected duration is unknown for that (path, digest) pair or the recorded source_duration differs. It binds the artifact's time references to the real length of the source media.

Solutions

  1. Update source_duration to the actual duration of the referenced media file (probe with ffprobe or the library's duration helper)
  2. Re-register the reference media so it appears in source_durations, then validate again
  3. If the reference media changed length, regenerate the artifact and receipt together
  4. Remove media_seconds provenance pointing at non-media references

Example fix

# before
{'time_basis': 'media_seconds', 'reference_times': [12.5], 'source_duration': 30.0}  # stale
# after: recompute from actual media
{'time_basis': 'media_seconds', 'reference_times': [12.5], 'source_duration': 41.7}
Defensive patterns

Strategy: validation

Validate before calling

expected = source_durations.get((src['reference_path'], src['reference_sha256']))
assert expected is not None and src.get('source_duration') == expected, 'source_duration unbound'

Type guard

def duration_is_bound(src: dict, source_durations: dict) -> bool:
    expected = source_durations.get((src.get('reference_path'), src.get('reference_sha256')))
    return expected is not None and src.get('source_duration') == expected

Try / catch

try:
    validate_artifact_receipt(out_dir)
except ContractError as e:
    if 'unbound source duration' in str(e):
        refresh_source_durations(receipt, out_dir)
    else:
        raise

Prevention

When it happens

Trigger: A media_seconds provenance source whose source_duration is missing, stale after the reference media was re-encoded to a different length, or the reference is not in source_durations (e.g. not a recognized media file).

Common situations: Trimming or re-encoding a reference video without regenerating the receipt, copying receipts across bundles where the duration table differs, typos in source_duration values, referencing a non-media file.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

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

            if not isinstance(source.get("reference_path"), str) or not source["reference_path"]:
                raise ContractError(f"artifact {relative} has invalid reference path")
            digest = source.get("reference_sha256")
            if not isinstance(digest, str) or len(digest) != 64:
                raise ContractError(f"artifact {relative} has invalid reference SHA-256")
            if (source["reference_path"], digest) not in known_sources:
                raise ContractError(f"artifact {relative} cites an unknown provenance source")
            times = source.get("reference_times")
            basis = source.get("time_basis")
            if not isinstance(times, list) or basis not in {"media_seconds", "whole_file"}:
                raise ContractError(f"artifact {relative} has invalid reference/time provenance")
            if basis == "media_seconds" and not times:
                raise ContractError(f"artifact {relative} lacks media reference times")
            if basis == "whole_file" and times:
                raise ContractError(f"artifact {relative} whole-file provenance must not invent times")
            if basis == "media_seconds":
                expected_duration = source_durations.get((source["reference_path"], digest))
                if expected_duration is None or source.get("source_duration") != expected_duration:
                    raise ContractError(f"artifact {relative} has an unbound source duration")
                for time in times:
                    _validate_media_time(
                        time, expected_duration,
                        label=f"artifact {relative} media reference time",
                    )

    digest_payload = dict(receipt)
    claimed_digest = digest_payload.pop("receipt_sha256", None)
    actual_digest = hashlib.sha256(
        json.dumps(digest_payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
    ).hexdigest()
    if claimed_digest != actual_digest:
        raise ContractError("receipt SHA-256 does not match its canonical content")


def validate_bundle(out_dir: str | Path) -> None:
    """Validate required multimodal files and cross-artifact invariants."""
    out_dir = Path(out_dir)

View on GitHub (pinned to 8321021c54)