affaan-m/ECC · error · ValueError

modality must be image, video or 3d_asset

Error message

modality must be image, video or 3d_asset

What it means

Asset ingestion/validation requires each asset to declare a modality from the fixed set image, video, 3d_asset. The library throws this ValueError when `asset.get('modality')` is missing or not one of those exact strings, because downstream processing (bundle lookup, fingerprinting) dispatches on modality.

Solutions

  1. Set the asset's 'modality' field to exactly one of 'image', 'video', or '3d_asset'
  2. Check for typos/case differences in the manifest and fix them
  3. Print the offending asset dict (the error doesn't name it) to see the actual modality value
  4. If migrating from an older manifest format, rename legacy modality terms to the current tuple

Example fix

// before
{"id": "a1", "modality": "3d", "origin": "internal"}
// after
{"id": "a1", "modality": "3d_asset", "origin": "internal"}
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {"image", "video", "3d_asset"}
for a in assets:
    if a.get("modality") not in ALLOWED:
        raise ValueError(f"asset {a.get('id')}: modality must be one of {sorted(ALLOWED)}, got {a.get('modality')!r}")

Type guard

def is_valid_modality(m) -> bool:
    return m in ("image", "video", "3d_asset")

Try / catch

try:
    ingest_assets(assets)
except ValueError as e:
    if "modality must be" in str(e):
        log.error("asset with bad modality: %s", find_offending_asset(assets)); fix_and_retry()
    else:
        raise

Prevention

When it happens

Trigger: Calling ingest_assets or validate_assets with an asset dict lacking a 'modality' key, or with a value like 'image2', 'Image' (case mismatch), 'mesh', or None.

Common situations: Typos in hand-written asset manifests; older configs using a pre-rename modality name; programmatically generated assets that omit modality; YAML/JSON key casing mistakes ('Modality').

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/assets.py:100

        raise ValueError(f'Cannot read JSON object: {path}') from exc
    if not isinstance(data, dict):
        raise ValueError('Expected a JSON object')
    return data


def _assets(value: Any) -> list[dict[str, Any]]:
    if not isinstance(value, list) or not value:
        raise ValueError('assets must be a nonempty list')
    ids = set()
    for asset in value:
        if not isinstance(asset, dict):
            raise ValueError('Each asset must be an object')
        asset_id = _text(asset.get('id'), 'asset id')
        if asset_id in ids:
            raise ValueError(f'Duplicate asset id: {asset_id}')
        ids.add(asset_id)
        if asset.get('modality') not in tuple(MODALITIES):
            raise ValueError('modality must be image, video or 3d_asset')
        if asset.get('origin') not in tuple(ORIGINS):
            raise ValueError('Invalid asset origin')
    return value


def _provenance(asset: dict[str, Any], base: Path, verify: bool) -> dict[str, Any] | None:
    source = asset.get('provider_provenance')
    if asset['origin'] != 'external_result':
        if source is not None:
            raise ValueError('Provider provenance requires external_result origin')
        return None
    if not isinstance(source, dict):
        raise ValueError('external_result requires provider provenance and local evidence')
    provider = _text(source.get('provider'), 'provider')
    identifiers = {key: _text(source[key], key) for key in ('request_id', 'workflow_id')
                   if key in source}
    if not identifiers:
        raise ValueError('Provider provenance requires request_id or workflow_id')

View on GitHub (pinned to 8321021c54)