santifer/career-ops · error · Error
local parser returned invalid JSON
Error message
local parser returned invalid JSON
What it means
After execFileAsync runs the parser to completion, its stdout is passed to JSON.parse. If parsing fails (stdout is not valid JSON), this error fires. It is the parser-output contract: the parser must emit a single JSON document on stdout.
Source
Thrown at providers/local-parser.mjs:177
const parser = entry.parser || {};
const { command, args } = resolveInvocation(entry);
const timeout = Number(parser.timeout_ms || LOCAL_PARSER_TIMEOUT_MS);
const maxBuffer = Number(parser.max_buffer_bytes || LOCAL_PARSER_MAX_BUFFER_BYTES);
// cwd is pinned to the project root so a relative script arg resolves to the
// same file resolveInvocation() validated, regardless of the caller's cwd.
const { stdout } = await execFileAsync(command, args, {
cwd: PROJECT_ROOT,
timeout,
maxBuffer,
windowsHide: true,
});
let payload;
try {
payload = JSON.parse(stdout);
} catch {
throw new Error('local parser returned invalid JSON');
}
const rawJobs = Array.isArray(payload) ? payload : payload.jobs || payload.results;
if (!Array.isArray(rawJobs)) {
throw new Error('local parser JSON must be an array or contain jobs[]/results[]');
}
return rawJobs
.map(job => normalizeParserJob(job, entry))
.filter(Boolean);
}
/** @type {Provider} */
export default {
id: 'local-parser',
detect(entry) {
if (!entry.parser?.command) return null;View on GitHub (pinned to 9b17a8ac97)
Solutions
- Run the parser command manually with the same argv and inspect stdout — anything that isn't the JSON document must go to stderr.
- Move debug/logging in the parser to stderr (e.g. console.error / print(..., file=sys.stderr)).
- Increase parser.maxBuffer if the output was truncated (a parse error near the end often indicates truncation).
- Confirm the parser exits 0; a non-zero exit with stdout text will still hit JSON.parse and fail here.
Example fix
# before (parsers/acme.py)
import sys, json
print('starting parse') # pollutes stdout
print(json.dumps(jobs))
# after
import sys, json
print('starting parse', file=sys.stderr) # logs go to stderr
print(json.dumps(jobs)) # stdout is pure JSON Defensive patterns
Strategy: try-catch
Validate before calling
// Before trusting parser output, you can pre-validate by invoking the parser
// in a dry-run and checking stdout parses as JSON.
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFile);
export async function parserEmitsJson(command, args) {
try {
const { stdout } = await execFileAsync(command, args, { timeout: 5000, maxBuffer: 1 << 20 });
JSON.parse(stdout); return true;
} catch { return false; }
} Type guard
/** @param {string} stdout */
function isJsonDocument(stdout) {
try { JSON.parse(stdout); return true; } catch { return false; }
} Try / catch
try {
const jobs = await provider.fetch(entry, ctx);
results.push(...jobs);
} catch (err) {
if (err.message === 'local parser returned invalid JSON') {
console.warn(`parser ${entry.name} emitted non-JSON stdout — check for stray logs/truncation`);
}
throw err;
} Prevention
- Route all parser logging to stderr so stdout contains only the JSON document.
- Ensure the parser exits 0 on success; non-zero exits with stdout text will fail JSON.parse here.
- Size parser.maxBuffer to the expected output; truncated JSON is a common cause.
- Test the parser standalone (run the exact argv) and pipe stdout through a JSON validator before wiring it in.
When it happens
Trigger: The parser script printed non-JSON output: an error/stack trace to stdout, debug logging, an empty stdout, partial JSON, JSON followed by trailing text, or a different format (XML/HTML/CSV). stderr is ignored — only stdout is parsed.
Common situations: Parser has a stray console.log/print before the JSON; an exception dumped a traceback to stdout; the parser writes the array but appends a newline log line; the parser exited non-zero with an error message on stdout; buffer truncation (maxBuffer) cut the JSON mid-stream.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- local parser JSON must be an array or contain jobs[]/results
- Could not parse existing candidates file at ${candidatesPath
- Existing candidates file at ${candidatesPath} is not a JSON
- landingjobs: unexpected API response — expected a JSON array
- local-parser: careers_url is not a valid URL: ${value}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/ee49ecfd76ea1b08.
Report an issue: GitHub.