affaan-m/ECC · error · ContractError

CV effect lacks a valid subject anchor

Error message

CV effect {event.get('effect')} lacks a valid subject anchor

What it means

ContractError (f-string) raised when a CV event declares requires_subject_anchor=true but its subject_anchor is not a dict containing all required keys: mode, target, source_ref_sha256, evidence_time, source_duration, lost_policy. The contract requires a structurally complete anchor before further semantic checks run.

Solutions

  1. Ensure subject_anchor is a dict with all six keys: mode, target, source_ref_sha256, evidence_time, source_duration, lost_policy.
  2. Use snake_case key names exactly as the contract specifies.
  3. Emit the anchor from the receipt/reference pipeline so source_ref_sha256 and source_duration are populated from real data.
  4. Add a pre-validation schema check that reports which required anchor keys are missing.

Example fix

// before
{"effect": "cv_blur", "requires_subject_anchor": true, "subject_anchor": {"mode": "object_track"}}
// after
{"effect": "cv_blur", "requires_subject_anchor": true, "subject_anchor": {"mode": "object_track", "target": "subject_0", "source_ref_sha256": "<digest>", "evidence_time": 1.2, "source_duration": 10.0, "lost_policy": "disable_effect_until_track_recovers"}}
Defensive patterns

Strategy: type-guard

Validate before calling

REQUIRED_ANCHOR_KEYS = {"mode", "target", "source_ref_sha256", "evidence_time", "source_duration", "lost_policy"}
for event in recipe["events"]:
    anchor = event.get("subject_anchor")
    if not isinstance(anchor, dict) or not REQUIRED_ANCHOR_KEYS.issubset(anchor):
        raise ValueError(f"{event.get('effect')}: subject_anchor incomplete")

Type guard

def is_valid_anchor_shape(anchor) -> bool:
    required = {"mode", "target", "source_ref_sha256", "evidence_time", "source_duration", "lost_policy"}
    return isinstance(anchor, dict) and required.issubset(anchor)

Try / catch

try:
    validate_effect_recipe(recipe, reference_durations)
except ContractError as exc:
    if "lacks a valid subject anchor" in str(exc):
        missing = REQUIRED_ANCHOR_KEYS - set(event["subject_anchor"] or {})
        raise ValueError(f"anchor missing keys: {missing}") from exc
    raise

Prevention

When it happens

Trigger: Calling validate_effect_recipe with subject_anchor set to null, a non-dict (e.g. string or list), or a dict missing one or more of the six required keys (commonly forgetting source_duration or lost_policy).

Common situations: Anchors partially filled in from a template; fields dropped during JSON round-tripping; upstream tooling that emits anchor as a flat structure instead of a nested dict; typos in key names such as sourceRefSha256.

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


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

Appendix: source

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

    if len(set(intervals)) <= 1:
        raise ContractError("stochastic schedule is periodic; intervals must vary")
    for period in range(1, len(intervals) // 2 + 1):
        if all(intervals[index] == intervals[index % period] for index in range(len(intervals))):
            raise ContractError("stochastic schedule is periodic; repeating interval cycle")
    if recipe.get("periodic") is not False:
        raise ContractError("effect recipe must explicitly declare periodic=false")
    for event in events:
        cv_effect = str(event.get("effect", "")).startswith("cv_")
        if cv_effect and event.get("requires_subject_anchor") is not True:
            raise ContractError(f"CV effect {event.get('effect')} must require a subject anchor")
        if event.get("requires_subject_anchor"):
            anchor = event.get("subject_anchor")
            required = {
                "mode", "target", "source_ref_sha256", "evidence_time",
                "source_duration", "lost_policy",
            }
            if not isinstance(anchor, dict) or not required.issubset(anchor):
                raise ContractError(f"CV effect {event.get('effect')} lacks a valid subject anchor")
            if anchor.get("mode") not in {"object_track", "point_track", "segmentation_track"}:
                raise ContractError(f"CV effect {event.get('effect')} has an invalid subject anchor")
            if anchor.get("lost_policy") != "disable_effect_until_track_recovers":
                raise ContractError(f"CV effect {event.get('effect')} must fail closed on anchor loss")
            _validate_media_time(
                anchor.get("evidence_time"), anchor.get("source_duration"),
                label="anchor evidence time",
            )
            if reference_durations is not None:
                digest = anchor.get("source_ref_sha256")
                expected_duration = reference_durations.get(digest) if isinstance(digest, str) else None
                if expected_duration is None or anchor.get("source_duration") != expected_duration:
                    raise ContractError("anchor evidence source duration is not bound to its receipt reference")
    for event in events:
        placement = event.get("placement")
        if not isinstance(placement, dict) or not {"safe_area", "max_coverage", "occlusion_policy"}.issubset(placement):
            raise ContractError(f"effect {event.get('effect')} lacks placement constraints")

View on GitHub (pinned to 8321021c54)