santifer/career-ops · error · Error
workable: invalid URL
Error message
workable: invalid URL: ${url} What it means
The workable provider validates any URL it is given via assertWorkableUrl. If the string cannot be parsed by the URL constructor (missing scheme, whitespace, malformed characters, empty value), it throws this error before any network request is made. This is the first of three checks, preceding the HTTPS and host-allowlist checks.
Solutions
- Prefix the value with https:// if the scheme is missing
- Trim the value and remove embedded whitespace/newlines/control characters
- Check for template placeholders that failed to substitute (${...}, {slug})
- Sanity-check in Node: new URL(url) must not throw before configuring it
Example fix
// before careers_url: apply.workable.com/acme // after careers_url: https://apply.workable.com/acme
Defensive patterns
Strategy: validation
Validate before calling
function isParsableUrl(url) {
try { new URL(url); return true; } catch { return false; }
}
// pre-check: isParsableUrl(company.careers_url) Type guard
const isUrl = (v) => typeof v === 'string' && (() => { try { new URL(v); return true; } catch { return false; } })(); Try / catch
try {
await workableProvider.fetch(entry, ctx);
} catch (err) {
if (err.message.startsWith('workable: invalid URL')) {
console.error(`Bad careers_url for ${entry.name}: ${err.message} — add https:// and remove whitespace`);
return;
}
throw err;
} Prevention
- Ensure every careers_url includes the https:// scheme when written to portals.yml or tracked_companies
- Normalize (trim, strip newlines) URL fields when importing from CSV/spreadsheets
- Check for unsubstituted template placeholders in generated config
- Run a startup validation pass that calls new URL() on every configured URL before any scan starts
When it happens
Trigger: A tracked_companies entry or caller passes a careers_url like 'apply.workable.com/acme' (no scheme), an empty or whitespace string, a URL containing raw spaces or control characters, or an interpolation artifact such as 'https://null/' style values that still parse — no wait, unparseable inputs only: no-scheme strings, '%zz' bad escapes, embedded newlines.
Common situations: Portals.yml entries missing 'https://', spreadsheet/CSV imports that broke URLs across cells or lines, template substitution leaving fragments like '${slug}' in the URL, trailing garbage appended by copy-paste.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- weworkremotely: invalid URL
- comeet: URL path must be the careers-api endpoint
- consider: needs an https careers_url on a public host
- ⚠️ Could not parse config/profile.yml
- csod: cannot resolve careersite URL for
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/d96bbc82a308f876.
Report an issue: GitHub.
Appendix: source
Thrown at providers/workable.mjs:114
throw lastErr;
}
// Process-wide serialization: apply.workable.com fronts every tenant on the
// same host, so this process never needs more than one in-flight request to
// it at a time.
let workableQueue = Promise.resolve();
function serialized(fn) {
const result = workableQueue.then(fn, fn);
workableQueue = result.then(() => undefined, () => undefined);
return result;
}
function assertWorkableUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`workable: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`workable: URL must use HTTPS: ${url}`);
if (!ALLOWED_WORKABLE_HOSTS.has(parsed.hostname)) {
throw new Error(`workable: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_WORKABLE_HOSTS].join(', ')}`);
}
return url;
}
/**
* Extract the account slug from a tracked_companies entry's careers_url.
* @returns {string|null}
*/
export function resolveWorkableSlug(entry) {
const raw = entry && typeof entry.careers_url === 'string' ? entry.careers_url : '';
if (!raw) return null;
let parsed;
try {
parsed = new URL(raw);View on GitHub (pinned to aac998c7ed)