santifer/career-ops · error · Error
local-parser: careers_url is not a valid URL: ${value}
Error message
local-parser: careers_url is not a valid URL: ${value} What it means
safeCareersUrl runs entry.careers_url through new URL() before interpolating it into the parser's argv. If the value cannot be parsed as an absolute URL, this error fires. The guard exists because the value becomes a CLI argument — an unparseable or schemeless string could be misread by the parser process.
Source
Thrown at providers/local-parser.mjs:29
const LOCAL_PARSER_TIMEOUT_MS = 20_000;
const LOCAL_PARSER_MAX_BUFFER_BYTES = 2_000_000;
// `parser.command` / `parser.script` come from portals.yml, which on a shared or
// template config is not fully trusted. The command must be a known interpreter
// or a file inside this project — never an arbitrary binary like `rm` or `curl`.
const PROJECT_ROOT = realpathSync(resolve(fileURLToPath(new URL('..', import.meta.url))));
const ALLOWED_INTERPRETERS = new Set(['python3', 'python', 'node', 'deno', 'bun', 'sh', 'bash']);
// `{careers_url}` and `{company}` are interpolated into the parser's argv. Validate
// them so an interpolated value can never be read as a CLI flag (argument injection).
function safeCareersUrl(value) {
if (!value) return '';
let url;
try {
url = new URL(String(value));
} catch {
throw new Error(`local-parser: careers_url is not a valid URL: ${value}`);
}
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
throw new Error(`local-parser: careers_url must be http(s): ${value}`);
}
return url.href;
}
function safeCompany(value) {
if (!value) return '';
const name = String(value).trim();
// execFile passes args verbatim (no shell), so the only injection risk is a
// value that begins like a CLI flag.
if (name.startsWith('-')) {
throw new Error(`local-parser: company name cannot start with '-': ${value}`);
}
return name;
}
View on GitHub (pinned to 9b17a8ac97)
Solutions
- Set careers_url to a full absolute URL, e.g. https://www.acme.com/careers.
- If the parser does not actually need the URL, remove the {careers_url} placeholder from parser.args (safeCareersUrl is only called when the placeholder is present).
- Trim whitespace/newlines from the config value.
Example fix
# before
- name: Acme
provider: local-parser
careers_url: acme.com/careers
parser: { command: python3, script: parsers/acme.py, args: ['{careers_url}'] }
# after
- name: Acme
provider: local-parser
careers_url: https://www.acme.com/careers
parser: { command: python3, script: parsers/acme.py, args: ['{careers_url}'] } Defensive patterns
Strategy: validation
Validate before calling
import { URL } from 'node:url';
export function isValidCareersUrl(value) {
if (!value) return false;
try { new URL(String(value)); return true; } catch { return false; }
}
// In the config loader, only validate when the parser template uses {careers_url}:
// if (entry.parser?.args?.some(a => String(a).includes('{careers_url}')) && !isValidCareersUrl(entry.careers_url)) failConfig(...); Type guard
/** @param {unknown} v */
function isAbsoluteUrl(v) {
return typeof v === 'string' && v.length > 0 && (() => { try { new URL(v); return true; } catch { return false; } })();
} Try / catch
try {
await provider.fetch(entry, ctx);
} catch (err) {
if (err.message.startsWith('local-parser:')) console.warn(`local-parser config error for ${entry.name}: ${err.message}`);
throw err;
} Prevention
- Only include {careers_url} in parser.args when you actually pass a real URL.
- Lint local-parser entries to ensure careers_url is a full absolute URL whenever the placeholder is used.
- Trim whitespace from config strings at load time.
When it happens
Trigger: entry.careers_url is empty-ish-but-present, relative ('/careers'), missing a scheme ('acme.com'), or contains characters the URL constructor rejects, AND the parser arg template contains {careers_url} (expansion only happens when the placeholder is used).
Common situations: A local-parser entry has a parser.args template using {careers_url} but careers_url is blank or relative; config was copy-pasted and the URL field left half-filled.
Related errors
- local-parser: careers_url must be http(s): ${value}
- local-parser: company name cannot start with '-': ${value}
- arbeitnow: invalid URL: ${url}
- ashby: invalid URL: ${url}
- bamboohr: invalid URL: ${url}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/3216a56453cd4270.
Report an issue: GitHub.