affaan-m/ECC · error · ValueError

Each asset must be an object

Error message

Each asset must be an object

What it means

Item-type guard in _assets: the assets value is a list, but one of its entries is not a JSON object. Each asset must be a dict with its own fields; the offending element is a scalar, string, or array.

Solutions

  1. Replace each string entry with an object containing at least id, modality, origin, and path fields.
  2. Match the schema used by existing working manifests in the project.
  3. Check any converter (YAML->JSON) output for scalar entries.

Example fix

// before
"assets": ["hero.png", "logo.png"]

// after
"assets": [{"id": "hero", "modality": "image", "origin": "local", "path": "hero.png"}]
Defensive patterns

Strategy: type-guard

Validate before calling

import json
data = json.load(open(path))
assert all(isinstance(a, dict) for a in data['assets']), 'every asset must be an object'

Type guard

def all_asset_objects(assets) -> bool:
    return isinstance(assets, list) and all(isinstance(a, dict) for a in assets)

Try / catch

try:
    validate_assets(path)
except ValueError as e:
    if str(e) == 'Each asset must be an object':
        convert_string_paths_to_asset_objects(path)

Prevention

When it happens

Trigger: An assets list containing bare strings (e.g., ["a.png"]) or mixed types, passed to ingest_assets or validate_assets.

Common situations: Writing paths as strings instead of asset objects; YAML converted to JSON producing scalars; a simplification attempt where users list filenames directly.

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/14718f6b3f7b3f77. Report an issue: GitHub.

Appendix: source

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

def _load(path: Path) -> dict[str, Any]:
    _fingerprint(path)
    try:
        data = json.loads(path.read_text(encoding='utf-8'))
    except (ValueError, OSError) as exc:
        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):

View on GitHub (pinned to 8321021c54)