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

  1. Prefix the value with https:// if the scheme is missing
  2. Trim the value and remove embedded whitespace/newlines/control characters
  3. Check for template placeholders that failed to substitute (${...}, {slug})
  4. 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

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


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)