affaan-m/ECC · error · ContractError

rule lacks time evidence

Error message

rule {rule.get('rule_id')} lacks time evidence

What it means

Each evidence item must carry a non-empty 'times' list of media timestamps so rules are anchored in time within the source. Missing, non-list, or empty 'times' raises this ContractError, and each time is further validated against 'source_duration'.

Solutions

  1. Add a non-empty 'times' list of finite numbers to each evidence item.
  2. Ensure each time value is finite (no NaN/inf) and within [0, source_duration].
  3. Set a valid 'source_duration' on the evidence item if it is missing, since times are validated against it.
  4. Fix the upstream timestamp extractor to emit at least one time per evidence item before building provenance.

Example fix

// before
item = {"reference_sha256": digest, "source_duration": 10.0, "times": []}
// after
item = {"reference_sha256": digest, "source_duration": 10.0, "times": [0.5, 2.25]}
Defensive patterns

Strategy: validation

Validate before calling

import math
def times_ok(item):
    times, dur = item.get("times"), item.get("source_duration")
    return (isinstance(times, list) and len(times) > 0
            and all(isinstance(t, (int, float)) and math.isfinite(t) for t in times)
            and isinstance(dur, (int, float)) and all(0 <= t <= dur for t in times))

Type guard

def evidence_is_time_anchored(item: dict) -> bool:
    import math
    t = item.get("times")
    return isinstance(t, list) and len(t) > 0 and all(
        isinstance(x, (int, float)) and math.isfinite(x) for x in t)

Try / catch

try:
    validate_provenance(payload)
except ContractError as e:
    if "lacks time evidence" in str(e):
        payload = reextract_timestamps(payload)
        validate_provenance(payload)
    else:
        raise

Prevention

When it happens

Trigger: Evidence item like {'reference_sha256': '<64-hex>'} with no 'times', times = None, or times = []; also times containing non-finite or out-of-duration values that fail _validate_media_time.

Common situations: Evidence recorded from a detector that produced no timestamps; a schema migration dropping the times field; timestamps accidentally placed at the rule level instead of per evidence item; NaN/inf times from float division by zero frame rate.

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/ee8f65a176dee368. Report an issue: GitHub.

Appendix: source

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

            raise ContractError(f"effect {event.get('effect')} lacks placement constraints")


def validate_provenance(payload: dict[str, Any]) -> None:
    """Require every declared rule to cite immutable, timestamped evidence."""
    rules = payload.get("rules")
    if not isinstance(rules, list) or not rules:
        raise ContractError("provenance must contain derived rules")
    for rule in rules:
        evidence = rule.get("evidence")
        if not isinstance(evidence, list) or not evidence:
            raise ContractError(f"rule {rule.get('rule_id')} lacks reference evidence")
        for item in evidence:
            digest = item.get("reference_sha256")
            if not isinstance(digest, str) or len(digest) != 64:
                raise ContractError(f"rule {rule.get('rule_id')} lacks immutable reference evidence")
            times = item.get("times")
            if not isinstance(times, list) or not times:
                raise ContractError(f"rule {rule.get('rule_id')} lacks time evidence")
            source_duration = item.get("source_duration")
            for time in times:
                _validate_media_time(
                    time, source_duration, label=f"rule {rule.get('rule_id')} time evidence"
                )


def validate_manifests(manifests_dir: str | Path) -> None:
    """Require image, video, and 3D-asset dry-run request manifests."""
    manifests_dir = Path(manifests_dir)
    found = {path.stem for path in manifests_dir.glob("*.json")} if manifests_dir.is_dir() else set()
    missing = _REQUIRED_MODALITIES - found
    if missing:
        raise ContractError(f"missing modality manifests: {sorted(missing)}")
    for modality in _REQUIRED_MODALITIES:
        payload = json.loads((manifests_dir / f"{modality}.json").read_text(encoding="utf-8"))
        if payload.get("modality") != modality or not payload.get("requests"):
            raise ContractError(f"invalid or empty {modality} manifest")

View on GitHub (pinned to 8321021c54)