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
- Replace each string entry with an object containing at least id, modality, origin, and path fields.
- Match the schema used by existing working manifests in the project.
- 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
- Use typed manifest builders instead of hand-written JSON
- Validate against a JSON Schema before ingest
- Check YAML-to-JSON converters preserve object structure
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
- assets must be a nonempty list
- invalid or empty manifest
- -32602
- artifact path must be a non-empty relative path
- built an invalid spec
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)