santifer/career-ops · error · Error
workable: invalid URL: ${url}
Error message
workable: invalid URL: ${url} What it means
assertWorkableUrl pins Workable requests to apply.workable.com (ALLOWED_WORKABLE_HOSTS). This branch fires when new URL(url) throws on a URL built from widgetUrlFor(slug) or feedUrlFor(slug). Because the slug is first validated by SLUG_RE (^[A-Za-z0-9][A-Za-z0-9_-]*$), the only realistic way to hit this is a maintainer breaking the URL templates — not a user-config error.
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 9b17a8ac97)
Solutions
- Restore the widget template to https://apply.workable.com/api/v1/widget/accounts/${slug}?details=true.
- Unit-test assertWorkableUrl(widgetUrlFor("acme")).
Example fix
// before
const widgetUrlFor = (slug) => `apply.workable.com/api/v1/widget/accounts/${slug}`;
// after
const widgetUrlFor = (slug) => `https://apply.workable.com/api/v1/widget/accounts/${slug}?details=true`; Defensive patterns
Strategy: validation
Validate before calling
for (const tpl of [widgetUrlFor, feedUrlFor]) {
try { new URL(tpl("acme")); }
catch { throw new Error("workable: URL template produced an unparseable URL"); }
} Type guard
const isAbsoluteUrl = (s) => { try { new URL(s); return true; } catch { return false; } }; Prevention
- Validate the slug via SLUG_RE before building any URL (already done).
- Keep the URL templates as module constants covered by a unit test.
When it happens
Trigger: A maintainer edits widgetUrlFor/feedUrlFor and drops the scheme or otherwise makes it unparseable; a bad merge. The slug is sanitized before interpolation, so crafted careers_url values cannot reach here.
Common situations: A refactor of the URL templates; running a patched fork.
Related errors
- workable: URL must use HTTPS: ${url}
- workable: untrusted hostname "${parsed.hostname}" — must be
- weworkremotely: invalid URL: ${url}
- weworkremotely: URL must use HTTPS: ${url}
- weworkremotely: untrusted hostname "${parsed.hostname}" - mu
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/d96bbc82a308f876.
Report an issue: GitHub.