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
- Set review_status to one of exactly "pending", "rejected", or "approved" for each candidate
- Run the review step and persist its result into the candidate JSON before building the bundle
- 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
- Default review_status to "pending" when creating candidate records so the field is never absent
- Keep the review UI's persisted values limited to the three allowed literals
- Validate the full candidate schema immediately after the review step writes results
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
- artifact has invalid modality binding
- assigneeKind must be 'agent' or 'human'.
- CV effect has an invalid subject anchor
- --host-provider must be anthropic, openai, or unknown
- Install module has unsupported targets
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)