affaan-m/ECC · error · ContractError
genre has invalid time evidence
Error message
genre {spec.get('number')} has invalid time evidence What it means
Under measured_features, the sample_times key (the only group checked) must be a list of time groups. This raise fires when sample_times exists but is not a list — e.g. a dict keyed by source name, a tuple-like JSON array mis-serialized, or a single group object passed directly. It is the first of the time-evidence structural checks.
Solutions
- Wrap the group(s) in a list: sample_times must be [{...}, ...].
- If groups are keyed by source, flatten the dict values into a list before assigning.
- If a single group was intended, wrap it: [group].
- Update the producer script that serializes sample_times to emit a JSON array.
Example fix
// before
spec["measured_features"]["sample_times"] = {"cam_a": [{"times": [1.0]}]}
// after
spec["measured_features"]["sample_times"] = [{"times": [1.0]}] Defensive patterns
Strategy: validation
Validate before calling
def sample_times_is_list(spec):
m = spec.get("measured_features") or {}
st = m.get("sample_times", [])
return isinstance(st, list) Type guard
def is_time_group_list(value) -> bool:
return isinstance(value, list) Try / catch
try:
validate_genre_specs(specs)
except ContractError as e:
if "invalid time evidence" in str(e):
m = spec["measured_features"]
if isinstance(m.get("sample_times"), dict):
m["sample_times"] = list(m["sample_times"].values())
raise Prevention
- Always serialize sample_times as a JSON array of group objects.
- Flatten per-source dicts into lists before assigning.
- Add a schema check in the tool that writes sample_times.
- Keep the contract shape (list of dicts) documented in the producer.
When it happens
Trigger: Calling validate_genre_specs / validate_bundle with measured_features.sample_times being a dict (e.g. {"src": [...]}) or a single group dict instead of a list of group dicts.
Common situations: Tooling that indexes sample times per source and writes the dict directly; a refactor changing sample_times from list to map without updating the contract; hand-authored configs nesting groups one level too deep.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Canonical session snapshot must be an object
- Canonical session snapshot requires
- Canonical session snapshot requires
- Canonical session snapshot requires
- explicit opacity must be finite within 0..100
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/7c080a51399d5bb3.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:147
for spec in specs:
if spec.get("dry_run") is not True:
raise ContractError(f"genre {spec.get('number')} crosses the dry-run boundary")
measured = spec.get("measured_features")
if measured is not None:
if not isinstance(measured, dict):
raise ContractError(f"genre {spec.get('number')} has invalid measured evidence")
_validate_numeric_evidence(measured, label=f"genre {spec.get('number')} evidence")
total_duration = measured.get("total_duration")
if not _is_finite_real(total_duration):
raise ContractError(f"genre {spec.get('number')} has invalid total duration")
total_duration = cast(float, total_duration)
if float(total_duration) <= 0:
raise ContractError(f"genre {spec.get('number')} has invalid total duration")
for group_name in ("sample_times",):
groups = measured.get(group_name, [])
if not isinstance(groups, list):
raise ContractError(f"genre {spec.get('number')} has invalid time evidence")
for group in groups:
if not isinstance(group, dict):
raise ContractError(f"genre {spec.get('number')} has invalid time evidence")
for time in group.get("times", []):
_validate_media_time(
time, group.get("source_duration"),
label=f"genre {spec.get('number')} time evidence",
)
temporal = measured.get("temporal", {})
if isinstance(temporal, dict):
for group in temporal.get("scene_change_evidence", []):
for time in group.get("times", []):
_validate_media_time(
time, group.get("source_duration"),
label=f"genre {spec.get('number')} scene evidence",
)
signature = spec.get("signature")
if not isinstance(signature, dict) or not _SIGNATURE_AXES.issubset(signature):View on GitHub (pinned to 8321021c54)