{"record":{"id":"81a3c9a462dceedb","repo":"Yeachan-Heo/oh-my-codex","slug":"autoresearch-candidate-artifact-notes-must-be-a-st","errorCode":null,"errorMessage":"autoresearch candidate artifact notes must be a string array","messagePattern":"autoresearch candidate artifact notes must be a string array","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/autoresearch/runtime.ts","lineNumber":1045,"sourceCode":"  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,\n    candidate_commit: record.candidate_commit,\n    base_commit: record.base_commit,\n    description: record.description,\n    notes: record.notes,\n    created_at: record.created_at,\n  };\n}\n\nasync function readCandidateArtifact(candidateFile: string): Promise<AutoresearchCandidateArtifact> {\n  if (!existsSync(candidateFile)) {\n    throw new Error(`autoresearch_candidate_missing:${candidateFile}`);\n  }","sourceCodeStart":1027,"sourceCodeEnd":1063,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/autoresearch/runtime.ts#L1027-L1063","documentation":"parseAutoresearchCandidateArtifact validates the JSON candidate artifact written at the end of an autoresearch run. This error means the 'notes' field is either not an array or contains at least one element that is not a string. It exists because downstream consumers assume notes is a clean string[] for display and ledger recording.","triggerScenarios":"Calling parseAutoresearchCandidateArtifact(raw) on a candidate artifact JSON whose 'notes' is missing, an object, or an array containing numbers/objects/null (e.g. {\"notes\": [1]} or {\"notes\": \"did stuff\"}).","commonSituations":"Hand-editing the candidate artifact file, a new version of the runner starting to write structured note objects instead of plain strings, or an external tool appending metadata into notes.","solutions":["Inspect the artifact JSON and confirm notes is an array of strings only","Fix the writer (or generator script) so it maps note objects to strings before serializing","If the file was hand-edited, replace non-string entries with strings and re-run","Upgrade/align the autoresearch runtime version that produced the artifact with the version parsing it"],"exampleFix":"// before\n{\"status\":\"candidate\",\"notes\":[{\"text\":\"built\"} ],...}\n\n// after\n{\"status\":\"candidate\",\"notes\":[\"built\"],...}","handlingStrategy":"validation","validationCode":"const raw = JSON.parse(text);\nif (!Array.isArray(raw.notes) || raw.notes.some((n) => typeof n !== 'string')) {\n  raw.notes = raw.notes.map((n) => (typeof n === 'string' ? n : JSON.stringify(n)));\n}","typeGuard":"function hasStringNotes(v: unknown): v is { notes: string[] } {\n  return typeof v === 'object' && v !== null && Array.isArray((v as any).notes) && (v as any).notes.every((n: unknown) => typeof n === 'string');\n}","tryCatchPattern":"try { parse(raw); } catch (e) { if ((e as Error).message.includes('notes must be a string array')) { /* normalize notes, retry */ } throw e; }","preventionTips":["Always generate candidate artifacts with the runner, never hand-edit them","Validate artifacts with a JSON schema before passing to the parser"],"tags":["autoresearch","json","validation","artifact"],"backgroundTag":"schema-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}