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

  1. Find the duplicated id in the candidates list and rename one entry to a unique value
  2. Regenerate the candidate list from the provider run so each item gets a fresh unique id
  3. 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

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


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] = item

View on GitHub (pinned to 8321021c54)