{"record":{"id":"169af603d7fece3d","repo":"affaan-m/ECC","slug":"bundle-changed-during-validation","errorCode":null,"errorMessage":"Bundle changed during validation","messagePattern":"Bundle changed during validation","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/assets.py","lineNumber":158,"sourceCode":"    from .contract import validate_bundle\n\n    if verify:\n        binding = _verify_binding(value, base)\n        root = Path(binding['path']).parent\n    else:\n        root = _path(value, base)\n        binding = _fingerprint(root / 'receipt.json')\n    validate_bundle(root)\n    requests = {}\n    for modality in sorted(MODALITIES):\n        manifest = _load(root / 'manifests' / f'{modality}.json')\n        for request in manifest['requests']:\n            key = (modality, _text(request.get('request_id'), 'request_id'))\n            if key in requests:\n                raise ValueError('Ambiguous duplicate bundle request')\n            requests[key] = request\n    if binding != _fingerprint(root / 'receipt.json'):\n        raise ValueError('Bundle changed during validation')\n    return binding, requests\n\n\ndef _taste(asset: dict[str, Any], requests: dict[Any, Any], verify: bool) -> dict[str, Any]:\n    if not requests:\n        if any(key in asset for key in (*_TASTE_FIELDS, 'request_id')):\n            raise ValueError('Taste claims require a validated bundle')\n        return {}\n    request_id = _text(asset.get('request_id'), 'bundle request_id')\n    request = requests.get((asset['modality'], request_id))\n    if request is None:\n        raise ValueError('Asset request_id/modality does not match the bundle')\n    result = dict(request_id=request_id, **{key: request[key] for key in _TASTE_FIELDS})\n    if verify:\n        if any(asset.get(key) != value for key, value in result.items()):\n            raise ValueError('Asset taste lineage differs from its bundle request')\n    elif any(key in asset for key in _TASTE_FIELDS):\n        raise ValueError('Taste fields are derived from the bundle, not supplied')","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/assets.py#L140-L176","documentation":"The library fingerprints receipt.json before and after reading the manifests; if the receipt's fingerprint differs from the binding computed at bundle open, the bundle was modified concurrently during validation and results can't be trusted.","triggerScenarios":"Another process writing to the bundle (regenerating artifacts, updating receipts) while validate_assets or ingest_assets is running; a watcher/sync tool touching receipt.json mid-validation.","commonSituations":"Running validation while a generation pipeline is still emitting into the same bundle directory; Dropbox/OneDrive sync replacing files during a read; two CI jobs validating and rebuilding the same bundle concurrently.","solutions":["Re-run validation once the bundle is quiescent (no writers active)","Lock or serialize the pipeline so nothing writes to the bundle during validation","Exclude the bundle directory from live sync/backup tools during validation runs"],"exampleFix":"// before\nvalidate_assets(...)  # while generation job still writing to bundle\n// after\nwait_for_generation_job_to_finish(); validate_assets(...)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        return validate_assets(assets, bundle_root=root)\n    except ValueError as e:\n        if str(e) == \"Bundle changed during validation\" and attempt < 2:\n            wait_until_bundle_quiescent(root); continue\n        raise","preventionTips":["Ensure no generation/writing jobs run against the bundle during validation","Pause cloud-sync clients for bundle directories during CI runs","Serialize validation and rebuild steps with a lock or job dependency"],"tags":["integrity","race-condition","bundle"],"backgroundTag":"checksum-mismatch","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"}