affaan-m/ECC · error · ValueError
Taste claims require a validated bundle
Error message
Taste claims require a validated bundle
What it means
Taste metadata fields (genre_number, genre_slug, style_fingerprint, reference_sha256) and request_id are only valid when the asset resolves against a loaded, validated bundle. When no bundle requests exist, the library throws this if the asset carries any of those keys, preventing unverifiable taste claims.
Solutions
- Validate/ingest against the bundle root that contains manifests/ and receipt.json so the taste claims can resolve
- Remove request_id and taste fields from assets that are intentionally outside a bundle
- Regenerate the bundle first if it was never created, then re-run with it
Example fix
// before
assets = [{"id": "a1", "modality": "image", "origin": "internal", "request_id": "r1"}]
validate_assets(assets) # no bundle
// after
assets = [{"id": "a1", "modality": "image", "origin": "internal"}]
validate_assets(assets, bundle_root="path/to/bundle") Defensive patterns
Strategy: validation
Validate before calling
TASTE_KEYS = {"genre_number", "genre_slug", "style_fingerprint", "reference_sha256", "request_id"}
if bundle_root is None:
for a in assets:
bad = TASTE_KEYS & a.keys()
if bad:
raise ValueError(f"asset {a['id']} has bundle-only keys {bad} but no bundle") Try / catch
try:
ingest_assets(assets)
except ValueError as e:
if str(e) == "Taste claims require a validated bundle":
strip_taste_keys(assets) or point_to_bundle(); retry()
else:
raise Prevention
- Only attach request_id/taste fields when validating against a real bundle
- Keep bundle-less asset exports in a schema that excludes taste keys
- Build bundles before exporting assets that reference them
When it happens
Trigger: Calling ingest_assets/validate_assets without a bundle directory (no manifests/receipt), while an asset contains request_id or any _TASTE_FIELDS key.
Common situations: Validating loose assets outside a bundle but keeping taste fields from an earlier export; forgetting to point the tool at the bundle root; assets exported before the bundle was generated.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Asset request_id/modality does not match the bundle
- incomplete application bundle
- missing provenance
- missing required bundle fields
- nonempty text required
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/d0796bef9645b5ad.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/assets.py:165
binding = _fingerprint(root / 'receipt.json')
validate_bundle(root)
requests = {}
for modality in sorted(MODALITIES):
manifest = _load(root / 'manifests' / f'{modality}.json')
for request in manifest['requests']:
key = (modality, _text(request.get('request_id'), 'request_id'))
if key in requests:
raise ValueError('Ambiguous duplicate bundle request')
requests[key] = request
if binding != _fingerprint(root / 'receipt.json'):
raise ValueError('Bundle changed during validation')
return binding, requests
def _taste(asset: dict[str, Any], requests: dict[Any, Any], verify: bool) -> dict[str, Any]:
if not requests:
if any(key in asset for key in (*_TASTE_FIELDS, 'request_id')):
raise ValueError('Taste claims require a validated bundle')
return {}
request_id = _text(asset.get('request_id'), 'bundle request_id')
request = requests.get((asset['modality'], request_id))
if request is None:
raise ValueError('Asset request_id/modality does not match the bundle')
result = dict(request_id=request_id, **{key: request[key] for key in _TASTE_FIELDS})
if verify:
if any(asset.get(key) != value for key, value in result.items()):
raise ValueError('Asset taste lineage differs from its bundle request')
elif any(key in asset for key in _TASTE_FIELDS):
raise ValueError('Taste fields are derived from the bundle, not supplied')
return result
def ingest_assets(config_path: str | Path, out_receipt: str | Path) -> dict[str, Any]:
"""Bind local assets/provenance/lineage; write a new receipt, never overwrite."""
config_file = _path(config_path, Path.cwd())
config = _load(config_file)View on GitHub (pinned to 8321021c54)