affaan-m/ECC · error · ValueError
Duplicate asset id
Error message
Duplicate asset id: {asset_id} What it means
_assets collects each asset's validated id into a set and raises 'Duplicate asset id: {asset_id}' when the same id appears more than once. Asset ids must be unique within a manifest because they are the identity key for provenance, binding, and bundling.
Solutions
- Rename the duplicate entries so each id is unique (e.g., hero-v1, hero-v2).
- Add a pre-flight dedupe check in any script that generates manifests.
- If the same file is needed twice, reference it by the same path but give distinct ids only if the tooling supports it — otherwise reuse one entry.
- Grep the manifest for repeated ids: python -c "import json,collections;d=json.load(open('m.json'));print([i for i,c in collections.Counter(a['id'] for a in d['assets']).items() if c>1])".
Example fix
// before
[{"id": "hero", ...}, {"id": "hero", "path": "hero-alt.png", ...}]
// after
[{"id": "hero", ...}, {"id": "hero-alt", "path": "hero-alt.png", ...}] Defensive patterns
Strategy: validation
Validate before calling
import json, collections
data = json.load(open(path))
ids = [a['id'] for a in data['assets']]
dupes = [i for i, c in collections.Counter(ids).items() if c > 1]
assert not dupes, f'duplicate ids: {dupes}' Try / catch
try:
ingest_assets(path)
except ValueError as e:
if str(e).startswith('Duplicate asset id'):
rekey_duplicates(path) Prevention
- Generate ids programmatically with a uniqueness guarantee (uuid or slug+counter)
- Add a dedupe assert in manifest generators
- When merging manifests, re-key ids from the incoming side
When it happens
Trigger: Two entries in the assets list share the same 'id' value — e.g., copy-pasted entries, or a merge of two manifests without re-keying ids.
Common situations: Duplicating an entry to add a second file but forgetting to change the id; programmatic manifest generation appending the same default id ('asset1') repeatedly; merging branches that each added an asset with the same id.
Related errors
- assets must be a nonempty list
- candidate ids must be unique
- Each asset must be an object
- Install module has invalid targets; expected an array of…
- Invalid : expected globally unique id
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/686a00e1849648c3.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/assets.py:97
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):
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')View on GitHub (pinned to 8321021c54)