{"record":{"id":"0581ab11a0f7523c","repo":"Yeachan-Heo/oh-my-codex","slug":"visual-verdict-field-must-be-an-array","errorCode":null,"errorMessage":"visual_verdict.${field} must be an array","messagePattern":"visual_verdict\\.(.+?) must be an array","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/visual/verdict.ts","lineNumber":24,"sourceCode":"\nexport interface VisualVerdict {\n  score: number;\n  verdict: VisualVerdictStatus;\n  category_match: boolean;\n  differences: string[];\n  suggestions: string[];\n  reasoning: string;\n}\n\nexport interface VisualLoopFeedback extends VisualVerdict {\n  threshold: number;\n  passes_threshold: boolean;\n  next_actions: string[];\n}\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('|')}`);","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/visual/verdict.ts#L6-L42","documentation":"parseVisualVerdict validates a visual_verdict object; asTrimmedStringArray throws when a field expected to be a string array (e.g. observations, next_actions) is not an Array. The parser strictly enforces the verdict schema before use.","triggerScenarios":"Calling parseVisualVerdict on a JSON object where visual_verdict.observations or visual_verdict.next_actions is a string, object, null, or undefined instead of an array.","commonSituations":"LLM/visual-agent output that emits a comma-separated string instead of an array; hand-edited verdict JSON; downstream producers whose schema drifted (e.g. renamed or restructured fields).","solutions":["Fix the producer to emit arrays for observations/next_actions (possibly empty arrays)","If wrapping model output, coerce string values into [string] before parsing","Add a schema check (e.g. zod) on the verdict producer side"],"exampleFix":"// before\n{ \"verdict\": \"fail\", \"observations\": \"header misaligned\", \"next_actions\": \"fix css\" }\n// after\n{ \"verdict\": \"fail\", \"observations\": [\"header misaligned\"], \"next_actions\": [\"fix css\"] }","handlingStrategy":"type-guard","validationCode":"for (const f of ['observations','next_actions']) if (!Array.isArray(verdict[f])) verdict[f] = [verdict[f]].filter(v => v != null).map(String);","typeGuard":"const isStringArray = (v: unknown): v is string[] => Array.isArray(v) && v.every(x => typeof x === 'string');","tryCatchPattern":null,"preventionTips":["Validate visual_verdict with a schema (zod/json-schema) at the producer boundary","Make model output pass through a normalizer that coerces scalars to arrays"],"tags":["visual-verdict","schema-validation","json","parsing"],"backgroundTag":"schema-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}