{"record":{"id":"5038e568f13ec38b","repo":"santifer/career-ops","slug":"existing-candidates-file-at-candidatespath-is-n","errorCode":null,"errorMessage":"Existing candidates file at ${candidatesPath} is not a JSON array","messagePattern":"Existing candidates file at (.+?) is not a JSON array","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"paste-reply.mjs","lineNumber":125,"sourceCode":"  };\n}\n\n/**\n * Append a candidate to the candidates JSON file, creating the file/array if\n * missing, without disturbing any existing entries. Exported for direct unit\n * testing. Returns the total candidate count after the append.\n */\nexport function appendCandidate(candidate, candidatesPath = CANDIDATES_PATH) {\n  let candidates = [];\n  if (fs.existsSync(candidatesPath)) {\n    let parsed;\n    try {\n      parsed = JSON.parse(fs.readFileSync(candidatesPath, 'utf-8'));\n    } catch (e) {\n      throw new Error(`Could not parse existing candidates file at ${candidatesPath}: ${e.message}`);\n    }\n    if (!Array.isArray(parsed)) {\n      throw new Error(`Existing candidates file at ${candidatesPath} is not a JSON array`);\n    }\n    candidates = parsed;\n  } else {\n    fs.mkdirSync(path.dirname(candidatesPath), { recursive: true });\n  }\n  candidates.push(candidate);\n  // Write-then-rename so an interrupted write (crash, signal, disk full)\n  // can never leave the real candidates file truncated/corrupted.\n  const tmpPath = `${candidatesPath}.tmp`;\n  fs.writeFileSync(tmpPath, JSON.stringify(candidates, null, 2), 'utf-8');\n  renameSyncWithRetry(tmpPath, candidatesPath);\n  return candidates.length;\n}\n\n// Collect subject/from/body from stdin via a single readline.Interface and a\n// tiny manual state machine driven off its 'line' event.\n//\n// Earlier drafts chained `rl.question()` calls (one interface per prompt, or","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/paste-reply.mjs#L107-L143","documentation":"appendCandidate expects data/reply-candidates.json to be a JSON array of candidate entries. If the file parses as valid JSON but holds any other type (object, string, number, null), it throws this error instead of overwriting the unexpected shape.","triggerScenarios":"appendCandidate(candidate) called when data/reply-candidates.json contains a JSON object (e.g. {\"candidates\": [...]}) or any non-array JSON value.","commonSituations":"Another tool or script rewrote the file as a wrapper object; user hand-wrapped the array in an object; file was seeded from a different project's format; null literal written by a failed previous run.","solutions":["Inspect the file and unwrap the array (e.g. replace {\"candidates\":[...]} with the inner [...])","Convert the top-level value to a plain JSON array before re-running paste-reply","Restore from backup/git if the file was clobbered by another tool","If the wrapper format is intentional, migrate the entries into the flat-array format paste-reply expects"],"exampleFix":"// before\n{\"candidates\": [{\"from\": \"recruiter@acme.com\"}]}\n// after\n[{\"from\": \"recruiter@acme.com\"}]","handlingStrategy":"validation","validationCode":"const v = JSON.parse(readFileSync(p, 'utf-8')); if (!Array.isArray(v)) throw new Error('reply-candidates.json must be a top-level array');","typeGuard":"const isCandidateFile = (v) => Array.isArray(v) && v.every((e) => e && typeof e === 'object');","tryCatchPattern":"try { appendCandidate(c); } catch (e) { if (e.message.includes('is not a JSON array')) { console.error('Unwrap the file to a top-level JSON array, then retry'); } else throw e; }","preventionTips":["Keep the file a flat JSON array; never wrap it in an object","After external tools write the file, verify Array.isArray on parse","Restore from git if another tool reformatted it","Add a schema check in scripts that share this file"],"tags":["json","schema","validation"],"backgroundTag":"unexpected-response-shape","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}