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

  1. Make measured_features a dict of numeric evidence, e.g. {"total_duration": 180.0, ...}.
  2. If you have a JSON string, json.loads() it before assigning.
  3. If you have a list, restructure it into a keyed dict matching the expected evidence schema.
  4. 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

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


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)