affaan-m/ECC · error · ContractError
genre has invalid measured evidence
Error message
genre {spec.get('number')} has invalid measured evidence What it means
If a genre spec includes measured_features, that value must be a dict of numeric evidence. The validator raises this ContractError when measured_features is present but not a mapping (e.g. a list, string, or scalar). Setting it to None skips the check, so only non-null non-dict values trigger this.
Solutions
- Make measured_features a dict of numeric evidence, e.g. {"total_duration": 180.0, ...}.
- If you have a JSON string, json.loads() it before assigning.
- If you have a list, restructure it into a keyed dict matching the expected evidence schema.
- If there is genuinely no measurement yet, remove the key or set it to null instead of passing an empty/wrong-typed value.
Example fix
// before
spec["measured_features"] = json.dumps({"total_duration": 90.0})
// after
spec["measured_features"] = {"total_duration": 90.0} Defensive patterns
Strategy: type-guard
Validate before calling
def measured_is_dict(spec):
m = spec.get("measured_features")
return m is None or isinstance(m, dict) Type guard
def is_measured_features(value) -> bool:
return value is None or isinstance(value, dict) Try / catch
try:
validate_genre_specs(specs)
except ContractError as e:
if "invalid measured evidence" in str(e):
spec = next(s for s in specs if not isinstance(s.get("measured_features"), (dict, type(None))))
spec["measured_features"] = json.loads(spec["measured_features"])
raise Prevention
- Parse measurement JSON at ingestion; never store it as a string.
- Use None (not {} or []) when evidence is absent.
- Type-check measured_features where it is produced, not only at validation.
- Keep the evidence schema documented next to the producer code.
When it happens
Trigger: Calling validate_genre_specs / validate_bundle with a spec whose measured_features is a list of measurements, a JSON string of measurements, or a scalar number instead of a dict.
Common situations: A measurement tool serializing evidence as a JSON string instead of a parsed object; refactored code that switched measured_features from dict to list; hand-edited configs where quotes around the JSON block were left in place.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 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/2a21f15bda9f822b.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:136
"""Require complete, semantically distinct numbered genre specs."""
if not specs:
raise ContractError("at least one genre spec is required")
numbers = [spec.get("number") for spec in specs]
if len(numbers) != len(set(numbers)):
raise ContractError("genre numbers must be distinct")
fingerprints = [spec.get("style_fingerprint") for spec in specs]
signatures = [_semantic_signature(spec) for spec in specs]
if len(fingerprints) != len(set(fingerprints)) or len(signatures) != len(set(signatures)):
raise ContractError("genre references collapsed into a generic style; distinct specs required")
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",View on GitHub (pinned to 8321021c54)