{"record":{"id":"8f4c9e0c56b3216a","repo":"santifer/career-ops","slug":"landingjobs-invalid-url-url","errorCode":null,"errorMessage":"landingjobs: invalid URL: ${url}","messagePattern":"landingjobs: invalid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/landingjobs.mjs","lineNumber":26,"sourceCode":"//     created_at, type, tags, gross_salary_low, gross_salary_high, ... }\n//\n// NOTE: the v1 feed carries no company-name field — the employer slug only\n// appears in the posting URL path (`https://landing.jobs/at/<slug>/<job>`), so\n// `company` is derived best-effort from that slug (humanized) and falls back to\n// the portal entry name. The whole active set is returned in one call.\n//\n// Wire in via a `job_boards:` entry with `provider: landingjobs`.\n\nconst FEED_URL = 'https://landing.jobs/api/v1/jobs';\nconst TRUSTED_HOST = 'landing.jobs';\n\n/** @param {string} url */\nfunction assertLandingUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`landingjobs: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`landingjobs: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`landingjobs: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n// NaN-safe Date.parse.\nfunction toEpochMs(value) {\n  if (typeof value !== 'string' || !value) return undefined;\n  const parsed = Date.parse(value);\n  return Number.isNaN(parsed) ? undefined : parsed;\n}\n\n/**\n * Derive a best-effort company name from a Landing.jobs posting URL.\n * Posting URLs are `https://landing.jobs/at/<slug>/<job>`; the `<slug>` is","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/landingjobs.mjs#L8-L44","documentation":"The landingjobs provider validates every URL before use via assertLandingUrl. When the URL cannot be parsed by the WHATWG URL constructor (new URL throws), the provider throws this error to fail fast instead of making a bogus network request. It guards against malformed config values in portals.yml and programmatic misuse of the provider.","triggerScenarios":"Calling assertLandingUrl (directly or via the provider's fetch path) with a string that new URL() rejects: an empty string, a URL missing a scheme (e.g. 'landingjobs.io/jobs'), whitespace, or otherwise malformed syntax.","commonSituations":"A portals.yml entry with an unquoted or partially filled careers_url, a template placeholder left unresolved (e.g. '${url}' literally in config), or code passing a path-only string instead of a full URL.","solutions":["Fix the URL string so it is absolute and well-formed, including the scheme: https://landingjobs.io/...","If it comes from config, check the portals.yml entry for typos, missing scheme, or unresolved placeholders.","Pre-validate with new URL(url) in a try/catch before passing it to the provider.","Log or print the exact value being passed — the error interpolates it verbatim, so compare against the expected value."],"exampleFix":"// before\nassertLandingUrl('landingjobs.io/jobs');\n// after\nassertLandingUrl('https://landingjobs.co/jobs');","handlingStrategy":"validation","validationCode":"function isValidUrl(u) { try { new URL(u); return true; } catch { return false; } }\nif (!isValidUrl(url)) throw new Error(`skipping provider: invalid URL ${url}`);","typeGuard":"function isHttpUrl(v) { return typeof v === 'string' && (() => { try { new URL(v); return true; } catch { return false; } })(); }","tryCatchPattern":"try {\n  provider.fetch(entry, ctx);\n} catch (e) {\n  if (e.message.startsWith('landingjobs: invalid URL')) {\n    console.error(`Bad config URL: ${e.message}`);\n    return null;\n  }\n  throw e;\n}","preventionTips":["Validate all careers_url values in portals.yml at startup with new URL()","Quote URLs in YAML and always include the scheme","Add a config lint step that rejects placeholder tokens like '${' in URLs","Fail soft per-entry so one bad URL doesn't abort the whole scan"],"tags":["url-validation","config","input-validation"],"backgroundTag":"invalid-url","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"}