affaan-m/ECC · error · ValueError
candidate ids must be unique
Error message
candidate ids must be unique
What it means
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.
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
Example fix
// before
[{"id": "cand-1", ...}, {"id": "cand-1", ...}]
// after
[{"id": "cand-1", ...}, {"id": "cand-2", ...}] Defensive patterns
Strategy: validation
Validate before calling
function assertUniqueCandidateIds(candidates) {
const seen = new Set();
for (const c of candidates) {
if (seen.has(c.id)) throw new Error(`duplicate candidate id: ${c.id}`);
seen.add(c.id);
}
} Try / catch
try {
buildApplicationBundle({ candidates, ...rest });
} catch (e) {
if (e.message.includes("candidate ids must be unique")) {
const deduped = dedupeAndRename(candidates);
return buildApplicationBundle({ candidates: deduped, ...rest });
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- a generated candidate cannot claim original-source identity
- Duplicate asset id
- Invalid : expected globally unique id
- -32602
- a claim token is required
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/8ae77557303fd721.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/integration.py:288
_range(item["range"], bounds)
_text(item["reason"])
if not any(_overlap(item["range"], [c["start"], c["end"]])
for key, clips in tracks.items() if key.startswith("video") for c in clips):
raise ValueError("protected interval has no original video stack")
return [{"track": track, "clip_index": index, "clip": copy.deepcopy(clip),
"clip_sha256": _digest(clip)}
for track, clips in sorted(tracks.items()) for index, clip in enumerate(clips)
if any(_overlap(p["range"], [clip["start"], clip["end"]]) for p in protected)]
def _candidates(items: list, source_hash: str, input_hash: str, source_url: str) -> dict:
result = {}
for item in items:
_object(item, {"id", "media", "media_frames", "fps", "origin", "relationship",
"source_sha256", "compiled_input_sha256", "review_status", "generation_receipt"})
name = _text(item["id"])
if name in result:
raise ValueError("candidate ids must be unique")
_artifact(item["media"])
_integer(item["media_frames"], 1)
_rate(item["fps"])
if item["origin"] != "provider_generated" or item["relationship"] != "generated_variation":
raise ValueError("a generated candidate cannot claim original-source identity")
if item["source_sha256"] != source_hash or item["compiled_input_sha256"] != input_hash:
raise ValueError("candidate is bound to a different source or input")
if item["review_status"] not in ("pending", "rejected", "approved"):
raise ValueError("explicit candidate review state required")
evidence = _artifact(item["generation_receipt"], parse_json=True)
if not isinstance(evidence, dict) or not isinstance(evidence.get("request_id"), str):
raise ValueError("historical request evidence required")
_text(evidence["request_id"])
expected = {"source_sha256": source_hash, "compiled_input_sha256": input_hash,
"candidate_sha256": item["media"]["sha256"], "source_url": source_url}
if any(evidence.get(key) != value for key, value in expected.items()):
raise ValueError("historical evidence does not bind the candidate source/input/bytes")
result[name] = itemView on GitHub (pinned to 8321021c54)