{"record":{"id":"1b0a6e07aedf2061","repo":"santifer/career-ops","slug":"could-not-parse-existing-candidates-file-at-cand","errorCode":null,"errorMessage":"Could not parse existing candidates file at ${candidatesPath}: ${e.message}","messagePattern":"Could not parse existing candidates file at (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"paste-reply.mjs","lineNumber":122,"sourceCode":"    // Classification is reply-watch.mjs's job, not this script's — leaving\n    // signal unset is safe (see file header comment for why).\n    signal: null,\n  };\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","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/paste-reply.mjs#L104-L140","documentation":"appendCandidate appends a reply-candidate entry to data/reply-candidates.json, an array file. If the existing file exists but is not valid JSON, it throws with the path and the underlying SyntaxError message rather than silently truncating the user's data.","triggerScenarios":"appendCandidate(candidate) called while data/reply-candidates.json contains malformed JSON — truncated write, hand-edited syntax error, concatenation of two runs, encoding corruption.","commonSituations":"A previous paste-reply run was killed mid-write; the user manually edited the JSON and left a trailing comma; a merge of two files produced invalid syntax; the file was written by another tool with non-JSON content.","solutions":["Open the file at candidatesPath and fix the JSON syntax error reported in e.message","Validate with `node -e \"JSON.parse(require('fs').readFileSync('data/reply-candidates.json','utf8'))\"` to locate the error","If unrecoverable, back up the file and rebuild it as a valid JSON array of the entries you want to keep","Re-run the paste-reply command after the file parses"],"exampleFix":"// before\nappendCandidate(candidate); // throws on corrupt file\n// after\ntry {\n  appendCandidate(candidate);\n} catch (e) {\n  if (e.message.startsWith('Could not parse existing candidates file')) {\n    console.error('Fix or restore data/reply-candidates.json first:', e.message);\n  }\n}","handlingStrategy":"validation","validationCode":"function canParseJsonArray(p) { try { return Array.isArray(JSON.parse(readFileSync(p, 'utf-8'))); } catch { return false; } }","typeGuard":null,"tryCatchPattern":"try { appendCandidate(c); } catch (e) { if (e.message.includes('Could not parse existing candidates file')) { backup(candidatesPath); console.error('Repair data/reply-candidates.json before appending'); } else throw e; }","preventionTips":["Never hand-edit reply-candidates.json without re-validating JSON afterward","Use a JSON editor/validator when touching the file","Back up the file before bulk operations","Kill-write safely: write to a temp file and rename atomically in your own tooling"],"tags":["json","parse","file"],"backgroundTag":"json-parse-error","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"}