{"record":{"id":"13e2926ec639510e","repo":"Yeachan-Heo/oh-my-codex","slug":"autoresearch-candidate-artifact-status-must-be-can","errorCode":null,"errorMessage":"autoresearch candidate artifact status must be candidate|noop|abort|interrupted","messagePattern":"autoresearch candidate artifact status must be candidate\\|noop\\|abort\\|interrupted","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/autoresearch/runtime.ts","lineNumber":1033,"sourceCode":"    worktreePath: manifest.worktree_path,\n    taskDescription: `autoresearch resume ${runId}`,\n  };\n}\n\nexport function parseAutoresearchCandidateArtifact(raw: string): AutoresearchCandidateArtifact {\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(raw);\n  } catch {\n    throw new Error('autoresearch candidate artifact must be valid JSON');\n  }\n  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n    throw new Error('autoresearch candidate artifact must be a JSON object');\n  }\n  const record = parsed as Record<string, unknown>;\n  const status = record.status;\n  if (status !== 'candidate' && status !== 'noop' && status !== 'abort' && status !== 'interrupted') {\n    throw new Error('autoresearch candidate artifact status must be candidate|noop|abort|interrupted');\n  }\n  if (record.candidate_commit !== null && typeof record.candidate_commit !== 'string') {\n    throw new Error('autoresearch candidate artifact candidate_commit must be string|null');\n  }\n  if (typeof record.base_commit !== 'string' || !record.base_commit.trim()) {\n    throw new Error('autoresearch candidate artifact base_commit is required');\n  }\n  if (typeof record.description !== 'string') {\n    throw new Error('autoresearch candidate artifact description is required');\n  }\n  if (!Array.isArray(record.notes) || record.notes.some((note) => typeof note !== 'string')) {\n    throw new Error('autoresearch candidate artifact notes must be a string array');\n  }\n  if (typeof record.created_at !== 'string' || !record.created_at.trim()) {\n    throw new Error('autoresearch candidate artifact created_at is required');\n  }\n  return {\n    status,","sourceCodeStart":1015,"sourceCodeEnd":1051,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/autoresearch/runtime.ts#L1015-L1051","documentation":"The parsed artifact object's status field must be exactly one of 'candidate', 'noop', 'abort', or 'interrupted'. Any other value (including missing/undefined or a typo) is rejected.","triggerScenarios":"A worker writing status: 'success', 'done', 'Candidate' (case mismatch), or omitting status entirely in the candidate artifact JSON.","commonSituations":"Prompt/template drift where the worker invents a status name, version changes introducing a new status not yet supported, or case-sensitive mismatches from model output.","solutions":["Set status to one of candidate|noop|abort|interrupted exactly (lowercase) in the artifact","If a new status is genuinely needed, update the parser's allowed list and the worker template together","Validate status in the worker harness before writing the artifact file"],"exampleFix":"// before\n{\"status\":\"done\",\"base_commit\":\"abc\",\"description\":\"...\",\"notes\":[]}\n// after\n{\"status\":\"candidate\",\"base_commit\":\"abc\",\"description\":\"...\",\"notes\":[]}","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['candidate', 'noop', 'abort', 'interrupted']);\nconst parsed = JSON.parse(raw) as Record<string, unknown>;\nif (!ALLOWED.has(String(parsed.status))) {\n  throw new Error(`Invalid artifact status ${String(parsed.status)}; expected candidate|noop|abort|interrupted`);\n}","typeGuard":"function isCandidateStatus(v: unknown): v is 'candidate' | 'noop' | 'abort' | 'interrupted' {\n  return v === 'candidate' || v === 'noop' || v === 'abort' || v === 'interrupted';\n}","tryCatchPattern":"try { const a = parseAutoresearchCandidateArtifact(raw); }\ncatch (e) { if (e instanceof Error && e.message.includes('status must be candidate')) { /* normalize status casing/value and retry */ } throw e; }","preventionTips":["Enumerate the allowed statuses verbatim in the worker prompt/template","Lowercase and trim status before writing the artifact","Version-bump templates and parser together when adding new statuses"],"tags":["autoresearch","enum-validation","status","artifact"],"backgroundTag":"enum-value-invalid","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}