{"record":{"id":"964cab10e7cc23e7","repo":"affaan-m/ECC","slug":"assets-must-be-a-nonempty-list","errorCode":null,"errorMessage":"assets must be a nonempty list","messagePattern":"assets must be a nonempty list","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/assets.py","lineNumber":90,"sourceCode":"        if magic != b'glTF' or version != 2 or size != final.st_size:\n            raise ValueError(f'Invalid GLB magic, version or declared size: {path}')\n    return dict(path=str(path), bytes=final.st_size, sha256=digest.hexdigest())\n\n\ndef _load(path: Path) -> dict[str, Any]:\n    _fingerprint(path)\n    try:\n        data = json.loads(path.read_text(encoding='utf-8'))\n    except (ValueError, OSError) as exc:\n        raise ValueError(f'Cannot read JSON object: {path}') from exc\n    if not isinstance(data, dict):\n        raise ValueError('Expected a JSON object')\n    return data\n\n\ndef _assets(value: Any) -> list[dict[str, Any]]:\n    if not isinstance(value, list) or not value:\n        raise ValueError('assets must be a nonempty list')\n    ids = set()\n    for asset in value:\n        if not isinstance(asset, dict):\n            raise ValueError('Each asset must be an object')\n        asset_id = _text(asset.get('id'), 'asset id')\n        if asset_id in ids:\n            raise ValueError(f'Duplicate asset id: {asset_id}')\n        ids.add(asset_id)\n        if asset.get('modality') not in tuple(MODALITIES):\n            raise ValueError('modality must be image, video or 3d_asset')\n        if asset.get('origin') not in tuple(ORIGINS):\n            raise ValueError('Invalid asset origin')\n    return value\n\n\ndef _provenance(asset: dict[str, Any], base: Path, verify: bool) -> dict[str, Any] | None:\n    source = asset.get('provider_provenance')\n    if asset['origin'] != 'external_result':","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/assets.py#L72-L108","documentation":"_assets validates the manifest's assets field and requires it to be a list with at least one element. If assets is missing, null, not a list (e.g., a dict or string), or an empty list, this error is raised from ingest_assets or validate_assets.","triggerScenarios":"Manifest JSON has no 'assets' key, assets: null, assets: {}, assets: \"...\", or assets: [].","commonSituations":"Newly created template manifest with an empty assets array; key typo (\"asset\" instead of \"assets\"); a filter step that removed all assets leaving an empty list.","solutions":["Add at least one asset entry under the 'assets' key of the manifest.","Check for key typos — the field must be exactly 'assets'.","If no assets are expected, skip calling ingest_assets/validate_assets rather than passing an empty manifest.","Fix the upstream filter/generation step that produced an empty asset list."],"exampleFix":"// before\n{\"assets\": []}\n\n// after\n{\"assets\": [{\"id\": \"hero\", \"modality\": \"image\", \"origin\": \"local\", \"path\": \"hero.png\"}]}","handlingStrategy":"validation","validationCode":"import json\ndata = json.load(open(path))\nassets = data.get('assets')\nassert isinstance(assets, list) and len(assets) > 0, 'assets must be a nonempty list'","typeGuard":"def has_assets(v) -> bool:\n    return isinstance(v, dict) and isinstance(v.get('assets'), list) and len(v['assets']) > 0","tryCatchPattern":"try:\n    ingest_assets(path)\nexcept ValueError as e:\n    if str(e) == 'assets must be a nonempty list':\n        skip_ingest_or_fix_manifest(path)","preventionTips":["Verify manifest templates ship with a sample asset","Check for 'assets' key typos in generators","Skip library calls entirely when no assets exist"],"tags":["validation","manifest","schema"],"backgroundTag":"empty-required-field","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}