{"record":{"id":"d96bbc82a308f876","repo":"santifer/career-ops","slug":"workable-invalid-url-url","errorCode":null,"errorMessage":"workable: invalid URL: ${url}","messagePattern":"workable: invalid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/workable.mjs","lineNumber":114,"sourceCode":"  throw lastErr;\n}\n\n// Process-wide serialization: apply.workable.com fronts every tenant on the\n// same host, so this process never needs more than one in-flight request to\n// it at a time.\nlet workableQueue = Promise.resolve();\nfunction serialized(fn) {\n  const result = workableQueue.then(fn, fn);\n  workableQueue = result.then(() => undefined, () => undefined);\n  return result;\n}\n\nfunction assertWorkableUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`workable: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`workable: URL must use HTTPS: ${url}`);\n  if (!ALLOWED_WORKABLE_HOSTS.has(parsed.hostname)) {\n    throw new Error(`workable: untrusted hostname \"${parsed.hostname}\" — must be one of: ${[...ALLOWED_WORKABLE_HOSTS].join(', ')}`);\n  }\n  return url;\n}\n\n/**\n * Extract the account slug from a tracked_companies entry's careers_url.\n * @returns {string|null}\n */\nexport function resolveWorkableSlug(entry) {\n  const raw = entry && typeof entry.careers_url === 'string' ? entry.careers_url : '';\n  if (!raw) return null;\n  let parsed;\n  try {\n    parsed = new URL(raw);","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/workable.mjs#L96-L132","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ncareers_url: apply.workable.com/acme\n// after\ncareers_url: https://apply.workable.com/acme","handlingStrategy":"validation","validationCode":"function isParsableUrl(url) {\n  try { new URL(url); return true; } catch { return false; }\n}\n// pre-check: isParsableUrl(company.careers_url)","typeGuard":"const isUrl = (v) => typeof v === 'string' && (() => { try { new URL(v); return true; } catch { return false; } })();","tryCatchPattern":"try {\n  await workableProvider.fetch(entry, ctx);\n} catch (err) {\n  if (err.message.startsWith('workable: invalid URL')) {\n    console.error(`Bad careers_url for ${entry.name}: ${err.message} — add https:// and remove whitespace`);\n    return;\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["url-validation","parse-error","config"],"backgroundTag":"invalid-url-format","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}