{"record":{"id":"8ae77557303fd721","repo":"affaan-m/ECC","slug":"candidate-ids-must-be-unique","errorCode":null,"errorMessage":"candidate ids must be unique","messagePattern":"candidate ids must be unique","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/integration.py","lineNumber":288,"sourceCode":"        _range(item[\"range\"], bounds)\n        _text(item[\"reason\"])\n        if not any(_overlap(item[\"range\"], [c[\"start\"], c[\"end\"]])\n                   for key, clips in tracks.items() if key.startswith(\"video\") for c in clips):\n            raise ValueError(\"protected interval has no original video stack\")\n    return [{\"track\": track, \"clip_index\": index, \"clip\": copy.deepcopy(clip),\n             \"clip_sha256\": _digest(clip)}\n            for track, clips in sorted(tracks.items()) for index, clip in enumerate(clips)\n            if any(_overlap(p[\"range\"], [clip[\"start\"], clip[\"end\"]]) for p in protected)]\n\n\ndef _candidates(items: list, source_hash: str, input_hash: str, source_url: str) -> dict:\n    result = {}\n    for item in items:\n        _object(item, {\"id\", \"media\", \"media_frames\", \"fps\", \"origin\", \"relationship\",\n                       \"source_sha256\", \"compiled_input_sha256\", \"review_status\", \"generation_receipt\"})\n        name = _text(item[\"id\"])\n        if name in result:\n            raise ValueError(\"candidate ids must be unique\")\n        _artifact(item[\"media\"])\n        _integer(item[\"media_frames\"], 1)\n        _rate(item[\"fps\"])\n        if item[\"origin\"] != \"provider_generated\" or item[\"relationship\"] != \"generated_variation\":\n            raise ValueError(\"a generated candidate cannot claim original-source identity\")\n        if item[\"source_sha256\"] != source_hash or item[\"compiled_input_sha256\"] != input_hash:\n            raise ValueError(\"candidate is bound to a different source or input\")\n        if item[\"review_status\"] not in (\"pending\", \"rejected\", \"approved\"):\n            raise ValueError(\"explicit candidate review state required\")\n        evidence = _artifact(item[\"generation_receipt\"], parse_json=True)\n        if not isinstance(evidence, dict) or not isinstance(evidence.get(\"request_id\"), str):\n            raise ValueError(\"historical request evidence required\")\n        _text(evidence[\"request_id\"])\n        expected = {\"source_sha256\": source_hash, \"compiled_input_sha256\": input_hash,\n                    \"candidate_sha256\": item[\"media\"][\"sha256\"], \"source_url\": source_url}\n        if any(evidence.get(key) != value for key, value in expected.items()):\n            raise ValueError(\"historical evidence does not bind the candidate source/input/bytes\")\n        result[name] = item","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/integration.py#L270-L306","documentation":"In _candidates, each generated-candidate entry must carry a unique string 'id'; the function collects them into a dict keyed by id. A duplicate id means two candidates would silently overwrite each other, so TasteForge raises immediately. Candidate ids become the join key between candidates and insert proposals.","triggerScenarios":"Calling build_application_bundle with a candidates list where two items share the same 'id' string. Typically caused by a generation batch reusing template ids (e.g. \"cand-1\") or appending a regenerated candidate without renaming.","commonSituations":"Re-running a provider generation job and merging results into an existing candidate list; hand-editing candidate JSON and duplicating a block; scripted pipelines that index candidates by frame offset which collides after retry.","solutions":["Find the duplicated id in the candidates list and rename one entry to a unique value","Regenerate the candidate list from the provider run so each item gets a fresh unique id","Add a pre-flight dedupe that errors on duplicate ids before calling build_application_bundle"],"exampleFix":"// before\n[{\"id\": \"cand-1\", ...}, {\"id\": \"cand-1\", ...}]\n// after\n[{\"id\": \"cand-1\", ...}, {\"id\": \"cand-2\", ...}]","handlingStrategy":"validation","validationCode":"function assertUniqueCandidateIds(candidates) {\n  const seen = new Set();\n  for (const c of candidates) {\n    if (seen.has(c.id)) throw new Error(`duplicate candidate id: ${c.id}`);\n    seen.add(c.id);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  buildApplicationBundle({ candidates, ...rest });\n} catch (e) {\n  if (e.message.includes(\"candidate ids must be unique\")) {\n    const deduped = dedupeAndRename(candidates);\n    return buildApplicationBundle({ candidates: deduped, ...rest });\n  }\n  throw e;\n}","preventionTips":["Generate candidate ids from a UUID or monotonic counter, never from frame offsets","Dedupe candidate lists when merging generation batches","Run the uniqueness check in the pipeline before persisting candidates"],"tags":["validation","uniqueness","candidates"],"backgroundTag":"invalid-argument-value","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"}