{"record":{"id":"ed6311ca96ee4de3","repo":"Yeachan-Heo/oh-my-codex","slug":"visual-verdict-verdict-must-be-one-of-visual-ve","errorCode":null,"errorMessage":"visual_verdict.verdict must be one of: ${VISUAL_VERDICT_STATUSES.join('|')}","messagePattern":"visual_verdict\\.verdict must be one of: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/visual/verdict.ts","lineNumber":38,"sourceCode":"}\n\nfunction asTrimmedStringArray(value: unknown, field: string): string[] {\n  if (!Array.isArray(value)) {\n    throw new Error(`visual_verdict.${field} must be an array`);\n  }\n  return value\n    .map((item) => {\n      if (typeof item !== 'string') {\n        throw new Error(`visual_verdict.${field} must contain strings`);\n      }\n      return item.trim();\n    })\n    .filter((item) => item.length > 0);\n}\n\nfunction parseVisualVerdictStatus(value: unknown): VisualVerdictStatus {\n  if (typeof value !== 'string') {\n    throw new Error(`visual_verdict.verdict must be one of: ${VISUAL_VERDICT_STATUSES.join('|')}`);\n  }\n  const normalized = value.trim().toLowerCase();\n  if (!VISUAL_VERDICT_STATUSES.includes(normalized as VisualVerdictStatus)) {\n    throw new Error(`visual_verdict.verdict must be one of: ${VISUAL_VERDICT_STATUSES.join('|')}`);\n  }\n  return normalized as VisualVerdictStatus;\n}\n\nexport function parseVisualVerdict(input: unknown): VisualVerdict {\n  if (!input || typeof input !== 'object') {\n    throw new Error('visual_verdict must be an object');\n  }\n  const raw = input as Record<string, unknown>;\n  if (typeof raw.score !== 'number' || !Number.isInteger(raw.score) || raw.score < 0 || raw.score > 100) {\n    throw new Error('visual_verdict.score must be an integer between 0 and 100');\n  }\n  if (typeof raw.category_match !== 'boolean') {\n    throw new Error('visual_verdict.category_match must be a boolean');","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/visual/verdict.ts#L20-L56","documentation":"parseVisualVerdictStatus requires visual_verdict.verdict to be a string that, after trim+lowercase, is one of VISUAL_VERDICT_STATUSES. It throws the same message both for non-string values and for unrecognized status strings, listing the allowed statuses in the message.","triggerScenarios":"Calling parseVisualVerdict with verdict missing/null/object, or a value like 'Fail ' that normalizes fine but 'FAILED', 'pass_with_issues', or any status not in VISUAL_VERDICT_STATUSES.","commonSituations":"LLM agents inventing new verdict labels; case mismatches handled by normalization but synonym mismatches not (e.g. 'needs_work' vs the allowed value); upstream schema versions that renamed statuses.","solutions":["Use exactly one of the allowed statuses listed in the error message (the message enumerates them)","Normalize model output by mapping synonyms to the canonical set before parsing","If a new status is genuinely needed, update VISUAL_VERDICT_STATUSES and re-publish both sides"],"exampleFix":"// before\n{ \"verdict\": \"FAILED\", \"observations\": [], \"next_actions\": [] }\n// after\n{ \"verdict\": \"fail\", \"observations\": [], \"next_actions\": [] } // using an allowed status from the error message","handlingStrategy":"validation","validationCode":"const v = String(verdict.verdict ?? '').trim().toLowerCase();\nif (!VISUAL_VERDICT_STATUSES.includes(v)) verdict.verdict = mapSynonym(v) ?? 'needs_review';","typeGuard":"const isVisualVerdictStatus = (v: unknown): v is VisualVerdictStatus =>\n  typeof v === 'string' && VISUAL_VERDICT_STATUSES.includes(v.trim().toLowerCase() as VisualVerdictStatus);","tryCatchPattern":null,"preventionTips":["Constrain LLM prompts to the exact allowed verdict enum","Map model synonyms to canonical statuses before parsing"],"tags":["visual-verdict","enum","status","validation"],"backgroundTag":"invalid-enum-value","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}