affaan-m/ECC · error · ContractError

missing modality manifests

Error message

missing modality manifests: {sorted(missing)}

What it means

validate_manifests requires a JSON manifest for every required modality (image, video, 3D asset) in the manifests directory. If one or more <modality>.json files are absent, this ContractError lists the missing stems so the bundle cannot pass without complete dry-run manifests.

Solutions

  1. Generate the missing <modality>.json manifest(s) into the manifests directory before validating.
  2. Check the manifests_dir path passed to validate_manifests/validate_bundle points at the directory that actually contains the files.
  3. Rename typo'd manifests to exactly match the required modality names (image.json, video.json, 3d_asset.json).
  4. Rerun the full bundle build so all modality manifest generators execute, then validate.

Example fix

// before
# manifests dir contains only image.json, video.json
// after
# add manifests/3d_asset.json with {"modality": "3d_asset", "requests": [...], "dry_run": true, ...}
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
REQUIRED = {"image", "video", "3d_asset"}
def manifests_ready(d):
    p = Path(d)
    found = {f.stem for f in p.glob("*.json")} if p.is_dir() else set()
    missing = REQUIRED - found
    if missing:
        raise FileNotFoundError(f"missing manifests: {sorted(missing)}")

Try / catch

try:
    validate_manifests(manifests_dir)
except ContractError as e:
    if "missing modality manifests" in str(e):
        generate_all_manifests(manifests_dir)  # rerun generation step
        validate_manifests(manifests_dir)
    else:
        raise

Prevention

When it happens

Trigger: Running validate_manifests('path/to/manifests') or validate_bundle() where the directory is missing entirely, is not a directory, or lacks one of image.json / video.json / 3d_asset.json (per _REQUIRED_MODALITIES).

Common situations: Forgetting to run the manifest-generation step before validation; manifests written to the wrong directory; a modality generator skipped due to an earlier failure; typo in a manifest filename (e.g. 'three_d.json'); empty manifests dir because generation was dry-run-skipped.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

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

            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")
        if (payload.get("dry_run") is not True or payload.get("submit") is not False
                or type(payload.get("provider_calls")) is not int
                or payload.get("provider_calls") != 0
                or payload.get("provider_execution") is not False):
            raise ContractError(f"{modality} manifest crosses the dry-run boundary")
        for request in payload["requests"]:
            if (request.get("dry_run") is not True
                    or request.get("submit") is not False
                    or type(request.get("provider_calls")) is not int
                    or request.get("provider_calls") != 0
                    or request.get("provider_execution") is not False
                    or request.get("provider_call_mode") != "disabled"):
                raise ContractError(f"{modality} request crosses the dry-run boundary")

View on GitHub (pinned to 8321021c54)