santifer/career-ops · error · Error
Could not parse existing candidates file at ${candidatesPath
Error message
Could not parse existing candidates file at ${candidatesPath}: ${e.message} What it means
Thrown by appendCandidate() in paste-reply.mjs when data/reply-candidates.json exists but JSON.parse fails on its contents. The function is an append-only writer using write-then-rename for crash safety; this guard stops it from appending onto a corrupted base file, which would propagate the corruption.
Source
Thrown at paste-reply.mjs:120
// Classification is reply-watch.mjs's job, not this script's — leaving
// signal unset is safe (see file header comment for why).
signal: null,
};
}
/**
* Append a candidate to the candidates JSON file, creating the file/array if
* missing, without disturbing any existing entries. Exported for direct unit
* testing. Returns the total candidate count after the append.
*/
export function appendCandidate(candidate, candidatesPath = CANDIDATES_PATH) {
let candidates = [];
if (fs.existsSync(candidatesPath)) {
let parsed;
try {
parsed = JSON.parse(fs.readFileSync(candidatesPath, 'utf-8'));
} catch (e) {
throw new Error(`Could not parse existing candidates file at ${candidatesPath}: ${e.message}`);
}
if (!Array.isArray(parsed)) {
throw new Error(`Existing candidates file at ${candidatesPath} is not a JSON array`);
}
candidates = parsed;
} else {
fs.mkdirSync(path.dirname(candidatesPath), { recursive: true });
}
candidates.push(candidate);
// Write-then-rename so an interrupted write (crash, signal, disk full)
// can never leave the real candidates file truncated/corrupted.
const tmpPath = `${candidatesPath}.tmp`;
fs.writeFileSync(tmpPath, JSON.stringify(candidates, null, 2), 'utf-8');
fs.renameSync(tmpPath, candidatesPath);
return candidates.length;
}
// Collect subject/from/body from stdin via a single readline.Interface and aView on GitHub (pinned to 9b17a8ac97)
Solutions
- Validate the file with a JSON linter to locate the syntax error, then fix or delete it.
- If unrecoverable, back up the corrupt file, delete it, and let appendCandidate recreate a fresh empty array.
- Avoid editing reply-candidates.json by hand — it is managed by paste-reply.mjs.
- Run `node -e "JSON.parse(require('fs').readFileSync('data/reply-candidates.json','utf8'))"` to confirm the fix.
Example fix
// before: file contains trailing comma -> throws on parse
[
{ ... },
]
// after: valid JSON
[
{ ... }
] Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'fs';
function candidatesFileValid(p) {
try { return Array.isArray(JSON.parse(readFileSync(p, 'utf8'))); }
catch { return false; }
}
if (!candidatesFileValid(p)) {
// repair or back up + delete before calling appendCandidate
} Type guard
null
Try / catch
try {
appendCandidate(c);
} catch (e) {
if (/Could not parse/.test(e.message)) {
// back up corrupt file, delete, retry
} else throw e;
} Prevention
- Never hand-edit data/reply-candidates.json — let paste-reply.mjs own it.
- Back up the file before any external sync tool touches it.
- Run a JSON validity check as part of CI/pre-commit.
When it happens
Trigger: The candidates file was partially written/truncated by a previous crash that bypassed the write-then-rename (e.g. an editor or external tool overwrote it directly); manual edit left invalid JSON; disk corruption; an encoding mismatch (BOM, CRLF mangled).
Common situations: A user hand-edited data/reply-candidates.json and broke the syntax; a sync tool (Dropbox/OneDrive) wrote a temp/conflict file over it; a previous version of the code wrote without the tmp+rename atomic pattern.
Related errors
- Existing candidates file at ${candidatesPath} is not a JSON
- hackernews: unexpected item response for thread ${threadId}
- himalayas: unexpected API response - expected { jobs: [...]
- ibm: unexpected API response — expected hits.hits[], got key
- jobicy: unexpected API response — expected { jobs: [...] },
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/1b0a6e07aedf2061.
Report an issue: GitHub.