santifer/career-ops · error · Error
local-parser: careers_url must be http(s): ${value}
Error message
local-parser: careers_url must be http(s): ${value} What it means
After safeCareersUrl parses the URL, it requires the protocol to be http: or https:. Any other scheme (file:, ftp:, data:, javascript:) is rejected. Unlike the cloud providers, http: is allowed here because an internal/intranet parser target may legitimately be plain HTTP.
Source
Thrown at providers/local-parser.mjs:32
// `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;
}
// Only validate a placeholder's value when the arg actually uses it — a fixed
// `parser.script` must not be rejected because some unrelated `{company}` value
// has punctuation it never sees.View on GitHub (pinned to 9b17a8ac97)
Solutions
- Use http:// or https:// for the careers_url value.
- If you intended to pass a local file path to the parser, do so via the parser script itself, not the {careers_url} placeholder.
- For intranet targets over plain HTTP, http:// is acceptable — just ensure the scheme is present.
Example fix
# before careers_url: file:///opt/feeds/acme.xml # after careers_url: http://intranet.acme.local/careers
Defensive patterns
Strategy: validation
Validate before calling
import { URL } from 'node:url';
export function isHttpOrHttps(value) {
try { const p = new URL(String(value)).protocol; return p === 'http:' || p === 'https:'; } catch { return false; }
} Type guard
/** @param {string} url */
function isHttpScheme(url) {
try { const p = new URL(url).protocol; return p === 'http:' || p === 'https:'; } catch { return false; }
} Try / catch
try {
await provider.fetch(entry, ctx);
} catch (err) {
if (err.message.includes('must be http(s)')) console.warn(`fix scheme for ${entry.name}: ${err.message}`);
throw err;
} Prevention
- Use http:// or https:// for any careers_url fed to {careers_url}.
- Never use file:// or other schemes as careers_url — pass local files via the parser script instead.
- Add a config lint rule that rejects non-http(s) schemes on local-parser careers_url values.
When it happens
Trigger: entry.careers_url parses but uses a non-http(s) scheme — e.g. file:///path/to/feed, ftp://..., or a data: URL — and the parser template uses {careers_url}.
Common situations: A local file:// path was configured as careers_url; an internal tool URL was mis-typed with the wrong scheme; a data: URL was used for a test fixture.
Related errors
- local-parser: careers_url is not a valid URL: ${value}
- local-parser: company name cannot start with '-': ${value}
- local-parser: the parser script must be the interpreter's fi
- Refusing non-HTTP(S) URL: ${url}
- refusing non-GitHub/unsafe repo URL: ${arg} (expected https:
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/a1e552d9b8b3d801.
Report an issue: GitHub.