affaan-m/ECC · error · ValueError

explicit candidate review state required

Error message

explicit candidate review state required

What it means

_candidates requires each candidate to carry an explicit 'review_status' that is one of 'pending', 'rejected', or 'approved'. Human review gating is central to TasteForge: generated footage may only enter the timeline through a recorded review decision, and a missing or unrecognized status breaks that gate.

Solutions

  1. Set review_status to one of exactly "pending", "rejected", or "approved" for each candidate
  2. Run the review step and persist its result into the candidate JSON before building the bundle
  3. Update the review tooling to emit TasteForge's canonical status values

Example fix

// before
{"id": "cand-7", "review_status": "ok", ...}
// after
{"id": "cand-7", "review_status": "approved", ...}
Defensive patterns

Strategy: validation

Validate before calling

const REVIEW_STATUSES = new Set(["pending", "rejected", "approved"]);
function assertReviewStatus(c) {
  if (!REVIEW_STATUSES.has(c.review_status))
    throw new Error(`candidate ${c.id} missing explicit review_status`);
}

Type guard

function hasReviewStatus(c) {
  return c != null && ["pending", "rejected", "approved"].includes(c.review_status);
}

Try / catch

try {
  buildApplicationBundle(cfg);
} catch (e) {
  if (e.message.includes("explicit candidate review state required")) {
    cfg.candidates = cfg.candidates.map(c => ({...c, review_status: c.review_status ?? "pending"}));
    return buildApplicationBundle(cfg);
  }
  throw e;
}

Prevention

When it happens

Trigger: Supplying a candidate whose 'review_status' is absent, null, empty, or an unexpected value like 'auto', 'approved_by_me', or 'ok' — anything outside the three allowed literals.

Common situations: A review tool that writes its own status vocabulary; candidates exported before the review step ran; hand-crafted candidate entries that omit the field; a schema/version change in the review pipeline.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/9666e8be9e794d73. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/integration.py:297


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
    return result


def _inserts(items: list, candidates: dict, config: dict, input_hash: str, edit_hash: str) -> None:
    occupied = []
    for item in items:
        _object(item, {"candidate_id", "candidate_range", "timeline_range", "retime", "approval_file"})
        candidate = candidates.get(_text(item["candidate_id"]))
        if candidate is None or candidate["review_status"] != "approved":

View on GitHub (pinned to 8321021c54)