{"record":{"id":"0ba1779792a4f849","repo":"santifer/career-ops","slug":"yourator-invalid-url-url","errorCode":null,"errorMessage":"yourator: invalid URL: ${url}","messagePattern":"yourator: invalid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/yourator.mjs","lineNumber":81,"sourceCode":"// Per the Job contract, postedAt is omitted rather than guessed — a synthesized\n// date would silently corrupt scan-ats-full.mjs's recency filtering.\n\nconst SITE_ORIGIN = 'https://www.yourator.co';\nconst FEED_BASE = `${SITE_ORIGIN}/api/v4/jobs`;\nconst TRUSTED_HOST = 'www.yourator.co';\n// Safety bound only — the loop stops on payload.hasMore. The live board was 88\n// pages on 2026-08-18; this leaves room to grow without silently truncating.\nconst DEFAULT_MAX_PAGES = 120;\nconst MAX_PAGES_CAP = 500;\nconst PAGE_DELAY_MS = 200;\n\n/** @param {string} url */\nfunction assertYouratorUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`yourator: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`yourator: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`yourator: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n/** Resolve the page cap: a positive integer `max_pages` on the entry, capped. */\nfunction resolveMaxPages(entry) {\n  const v = entry?.max_pages;\n  if (Number.isInteger(v) && v > 0) return Math.min(v, MAX_PAGES_CAP);\n  return DEFAULT_MAX_PAGES;\n}\n\n/**\n * Canonical URL for a posting — Source Indexing Policy rule 2, \"the shortest\n * verifiable path to the employer the source exposes\".","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/yourator.mjs#L63-L99","documentation":"Thrown by assertYouratorUrl() in providers/yourator.mjs when the URL given for a Yourator endpoint cannot be parsed by new URL(). Like its wttj counterpart, the Yourator provider pins all requests to a trusted https host and fails fast on unparseable input before making any request.","triggerScenarios":"Calling the yourator provider with an empty, scheme-less (e.g. 'yourator.co/xxx'), whitespace-containing, or interpolated-undefined URL string so that URL construction throws.","commonSituations":"Company slug or base URL configured without a scheme in portals.yml, an env var left unset producing an empty/undefined URL, or a string-concatenation bug joining path parts incorrectly.","solutions":["Look at the literal url in the message to spot empty/undefined/scheme-less input.","Prefix the value with 'https://' if only the scheme is missing.","Fix the upstream config value (portals.yml entry or env var) so a full URL reaches the provider.","Pre-validate with new URL(url) at the config-load site to surface the problem earlier."],"exampleFix":"// before\nconst url = 'yourator.co/api/v3/jobs';\nassertYouratorUrl(url);\n// after\nconst url = 'https://yourator.co/api/v3/jobs';\nassertYouratorUrl(url);","handlingStrategy":"validation","validationCode":"function isParseableYouratorUrl(u) { try { return new URL(u).protocol === 'https:'; } catch { return false; } }\nif (!isParseableYouratorUrl(entry.yourator.baseUrl)) throw new Error(`yourator: bad baseUrl ${entry.yourator.baseUrl}`);","typeGuard":"function toYouratorUrl(v) {\n  try { const u = new URL(v); return u.protocol === 'https:' && u.hostname === 'yourator.co' ? u : null; } catch { return null; }\n}","tryCatchPattern":"try {\n  await youratorProvider.fetch(entry);\n} catch (e) {\n  if (e.message.startsWith('yourator: invalid URL')) {\n    console.warn(`Yourator entry misconfigured: ${e.message}`);\n    return;\n  }\n  throw e;\n}","preventionTips":["Store Yourator endpoints as complete https URLs in portals.yml.","Validate all provider URLs once at config load, not per request.","Avoid interpolating possibly-undefined values into URL strings."],"tags":["url-parsing","input-validation","yourator"],"backgroundTag":"invalid-url","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}