{"record":{"id":"571cd5153cb62e0a","repo":"Yeachan-Heo/oh-my-codex","slug":"autoresearch-candidate-artifact-must-be-valid-json","errorCode":null,"errorMessage":"autoresearch candidate artifact must be valid JSON","messagePattern":"autoresearch candidate artifact must be valid JSON","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/autoresearch/runtime.ts","lineNumber":1025,"sourceCode":"    instructionsFile: manifest.instructions_file,\n    manifestFile: manifest.manifest_file,\n    ledgerFile: manifest.ledger_file,\n    latestEvaluatorFile: manifest.latest_evaluator_file,\n    resultsFile: manifest.results_file,\n    stateFile: activeRunStateFile(projectRoot),\n    candidateFile: manifest.candidate_file,\n    repoRoot: manifest.repo_root,\n    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  }","sourceCodeStart":1007,"sourceCodeEnd":1043,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/autoresearch/runtime.ts#L1007-L1043","documentation":"parseAutoresearchCandidateArtifact JSON.parses the raw candidate artifact string and throws when parsing fails. The candidate artifact emitted by an autoresearch worker must be syntactically valid JSON before any field validation happens.","triggerScenarios":"Passing a candidate artifact string that is not parseable JSON: truncated output, markdown fences around JSON, an empty string, or a worker writing free-form text to the artifact file.","commonSituations":"LLM worker output with prose or code fences around the JSON, truncated stdout captured to a file, encoding issues/BOM, or a partially written artifact read while the worker was still writing it.","solutions":["Print/inspect the raw artifact string and find the JSON syntax error location","Strip any surrounding markdown fences or prose so the string is pure JSON","Ensure the artifact is fully flushed before reading it (write-then-rename or wait for worker exit)","If the worker persistently emits non-JSON, fix the worker prompt/template to require raw JSON only"],"exampleFix":"// before\nconst artifact = parseAutoresearchCandidateArtifact(rawLlmOutput); // raw contains ```json fences\n// after\nconst json = rawLlmOutput.replace(/^```(?:json)?\\n?/, '').replace(/\\n?```$/, '');\nconst artifact = parseAutoresearchCandidateArtifact(json);","handlingStrategy":"type-guard","validationCode":"function isParsableJson(s: string): boolean {\n  try { JSON.parse(s); return true; } catch { return false; }\n}\nif (!isParsableJson(raw)) throw new Error('Candidate artifact is not valid JSON; check worker output for fences/truncation.');","typeGuard":"function parseJsonSafe(s: string): unknown | undefined {\n  try { return JSON.parse(s); } catch { return undefined; }\n}","tryCatchPattern":"try { const a = parseAutoresearchCandidateArtifact(raw); }\ncatch (e) { if (e instanceof Error && e.message === 'autoresearch candidate artifact must be valid JSON') { /* strip fences, log raw excerpt, re-ask worker */ } throw e; }","preventionTips":["Have workers emit raw JSON only — no markdown fences or prose","Write artifacts atomically (temp file + rename) so partial writes are never read","Validate worker output with JSON.parse in the harness before persisting"],"tags":["autoresearch","json","parsing","artifact"],"backgroundTag":"json-parse-error","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}