{"record":{"id":"6a5c4a19bcc0ecd8","repo":"Yeachan-Heo/oh-my-codex","slug":"autoresearch-candidate-artifact-must-be-a-json-obj","errorCode":null,"errorMessage":"autoresearch candidate artifact must be a JSON object","messagePattern":"autoresearch candidate artifact must be a JSON object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/autoresearch/runtime.ts","lineNumber":1028,"sourceCode":"    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  }\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  }","sourceCodeStart":1010,"sourceCodeEnd":1046,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/autoresearch/runtime.ts#L1010-L1046","documentation":"After successful JSON parsing, parseAutoresearchCandidateArtifact requires the top-level value to be a non-null, non-array object. Arrays, strings, numbers, booleans, or null are rejected before field checks run.","triggerScenarios":"Passing a JSON artifact that parses to a primitive or array — e.g. a worker emitting a bare string, a JSON array of notes, or 'null' as the whole document.","commonSituations":"Workers wrapping the artifact in an array, emitting a quoted JSON string of the real object (double serialization), or defaulting to null/empty output on failure paths.","solutions":["Ensure the worker writes a single JSON object ({...}) as the artifact root","If double-serialized (artifact is a JSON string containing JSON), parse once more before handing it over","If the worker emits an array, wrap it in an object (e.g. { results: [...] }) to satisfy the contract","Add a pre-check with JSON.parse + typeof === 'object' in your harness to fail fast with better context"],"exampleFix":"// before\nconst artifact = parseAutoresearchCandidateArtifact('\"{\\\\\\\"status\\\\\\\":\\\\\\\"candidate\\\\\\\"}\"'); // parses to a string\n// after\nconst artifact = parseAutoresearchCandidateArtifact('{\"status\":\"candidate\",...}');","handlingStrategy":"type-guard","validationCode":"const parsed = parseJsonSafe(raw);\nif (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n  throw new Error('Candidate artifact root must be a JSON object.');\n}","typeGuard":"function isJsonObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try { const a = parseAutoresearchCandidateArtifact(raw); }\ncatch (e) { if (e instanceof Error && e.message === 'autoresearch candidate artifact must be a JSON object') { /* fix double serialization or array wrapping in worker output */ } throw e; }","preventionTips":["Give workers a concrete example artifact object to mimic","Avoid double JSON.stringify when serializing worker output","Check the root is an object in the harness before writing the artifact"],"tags":["autoresearch","json","shape-validation","artifact"],"backgroundTag":"json-schema-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}