affaan-m/ECC · error · ContractError

artifact lacks exact reference/time provenance

Error message

artifact {relative} lacks exact reference/time provenance

What it means

validate_artifact_receipt requires every artifact receipt entry to carry a non-empty provenance list describing the exact source references and times the artifact was derived from. This error is raised when the 'provenance' key is absent, not a list, or an empty list. The library refuses to bless artifacts with no traceable origin.

Solutions

  1. Add a provenance list to the receipt entry with at least one object containing reference_path, reference_sha256, reference_times, and time_basis
  2. Regenerate the artifact through tasteforge so provenance is captured automatically
  3. If the artifact truly has no source, generate it as an original (unbound) rather than a reference-derived artifact, per the library's rules
  4. Cross-check the provenance against known_sources assembled by validate_bundle

Example fix

# before
receipt['artifacts']['clip.mp4'] = {'bytes': 1024, 'sha256': '...', 'modalities': ['video']}
# after
receipt['artifacts']['clip.mp4'] = {
    'bytes': 1024, 'sha256': '...', 'modalities': ['video'],
    'provenance': [{'reference_path': 'refs/intro.mp4',
                    'reference_sha256': 'a'*64,
                    'time_basis': 'whole_file', 'reference_times': []}]
}
Defensive patterns

Strategy: validation

Validate before calling

p = receipt_entry.get('provenance')
assert isinstance(p, list) and len(p) > 0, 'receipt entry requires non-empty provenance list'

Type guard

def has_provenance(entry: dict) -> bool:
    p = entry.get('provenance')
    return isinstance(p, list) and len(p) > 0 and all(isinstance(s, dict) for s in p)

Try / catch

try:
    validate_artifact_receipt(out_dir)
except ContractError as e:
    if 'lacks exact reference/time provenance' in str(e):
        rebuild_provenance_for_entry(entry_name)
    else:
        raise

Prevention

When it happens

Trigger: Validating a bundle whose receipt entry lacks 'provenance', has provenance set to null/{} / string, or has provenance: []. Also hit when hand-written receipts omit provenance entirely for an artifact that in fact derives from a reference.

Common situations: Manually authoring a receipt and forgetting provenance; a generation step that skips reference capture; older receipts from before provenance became required; deleting provenance to silence an unknown-source error without replacing it with valid sources.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

        try:
            path.relative_to(out_dir)
        except ValueError as error:
            raise ContractError(f"artifact path escapes output directory: {relative}") from error
        if entry.get("provider_execution") is not False:
            raise ContractError(f"artifact {relative} permits provider execution")
        if not isinstance(entry.get("genre_numbers"), list):
            raise ContractError(f"artifact {relative} lacks genre binding")
        modalities = entry.get("modalities")
        if (not isinstance(modalities, list)
                or any(modality not in _REQUIRED_MODALITIES for modality in modalities)):
            raise ContractError(f"artifact {relative} has invalid modality binding")
        if entry.get("bytes") != path.stat().st_size:
            raise ContractError(f"artifact {relative} byte size does not match receipt")
        if entry.get("sha256") != _sha256(path):
            raise ContractError(f"artifact {relative} SHA-256 does not match receipt")
        provenance = entry.get("provenance")
        if not isinstance(provenance, list) or not provenance:
            raise ContractError(f"artifact {relative} lacks exact reference/time provenance")
        for source in provenance:
            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))

View on GitHub (pinned to 8321021c54)