{"record":{"id":"98011365bcb4c301","repo":"santifer/career-ops","slug":"pythonorg-invalid-url-url","errorCode":null,"errorMessage":"pythonorg: invalid URL: ${url}","messagePattern":"pythonorg: invalid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/pythonorg.mjs","lineNumber":26,"sourceCode":"// Python.org provider — the board-wide public RSS jobs feed at\n// https://www.python.org/jobs/feed/rss/ (official Python Software Foundation job board).\n// The feed is public, no-auth, and RSS 2.0 XML.\n//\n// Each <item> exposes <title> (typically \"{Role}, {Company}\"), <link>,\n// and <description> (the first line typically contains the location).\n//\n// Wire in via a `job_boards:` entry with `provider: pythonorg`.\n\nconst FEED_URL = 'https://www.python.org/jobs/feed/rss/';\nconst TRUSTED_HOST = 'python.org';\n\n/** @param {string} url */\nexport function assertPythonOrgUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`pythonorg: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`pythonorg: URL must use HTTPS: ${url}`);\n  const host = parsed.hostname.toLowerCase();\n  const trusted = host === TRUSTED_HOST || host.endsWith(`.${TRUSTED_HOST}`);\n  if (!trusted) {\n    throw new Error(`pythonorg: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n// NaN-safe Date.parse — `|| undefined` would also coerce a valid epoch 0.\nfunction toEpochMs(value) {\n  if (!value) return undefined;\n  const parsed = Date.parse(value);\n  return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction fallbackCompany(entry) {","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/santifer/career-ops/blob/e7abd431fce9348a95261acac9e0c14779c35df8/providers/pythonorg.mjs#L8-L44","documentation":"assertPythonOrgUrl validates any URL handed to the python.org jobs-feed provider. When the input cannot be parsed by the URL constructor at all (malformed syntax, missing scheme), it throws 'pythonorg: invalid URL: {url}'. It is the first of three checks, before the HTTPS and trusted-host checks, so configuration typos surface as a clear provider-namespaced error.","triggerScenarios":"Calling assertPythonOrgUrl with a string the URL constructor rejects — e.g. 'python.org/jobs/feed/rss/' (no scheme), 'https://' (empty host), 'not a url', or a non-string coerced to something unparseable.","commonSituations":"A portals.yml job_boards entry with a typo'd or scheme-less URL; reading the URL from an env var that is unset or partially interpolated; concatenating base + path strings incorrectly; passing user-supplied input unvalidated.","solutions":["Fix the URL string so it is a valid absolute URL, e.g. 'https://www.python.org/jobs/feed/rss/'","Add the missing scheme (https://) — the most common cause is a scheme-less host:port/path string","Log/inspect the exact value passed in; unset or malformed env/config values are frequent culprits","Wrap calls in a check like `new URL(url)` (or the defense validation code) before invoking the provider"],"exampleFix":"// before\nassertPythonOrgUrl(config.feedUrl); // 'python.org/jobs/feed/rss/'\n// after\nconst feedUrl = new URL(config.feedUrl).href; // throws with a clear syntax error, or fix config to 'https://www.python.org/jobs/feed/rss/'\nassertPythonOrgUrl(feedUrl);","handlingStrategy":"validation","validationCode":"export function isValidPythonOrgUrl(url) {\n  try { new URL(url); return true; } catch { return false; }\n}\n// call before the provider: if (!isValidPythonOrgUrl(cfg.feedUrl)) throw new Error(`bad feedUrl: ${cfg.feedUrl}`)","typeGuard":"function isAbsoluteHttpUrl(value) {\n  if (typeof value !== 'string') return false;\n  try { const u = new URL(value); return u.protocol === 'http:' || u.protocol === 'https:'; } catch { return false; }\n}","tryCatchPattern":"try {\n  assertPythonOrgUrl(cfg.feedUrl);\n} catch (e) {\n  if (e.message.startsWith('pythonorg: invalid URL')) {\n    console.error(`Config error: feedUrl '${cfg.feedUrl}' is not a parseable absolute URL — add the https:// scheme`);\n    process.exitCode = 1;\n  } else throw e;\n}","preventionTips":["Always store feed URLs as full absolute strings including the scheme in portals.yml","Validate all job-board URLs at config-load time with `new URL()` so failures surface once, early","Watch for env-var interpolation producing empty/partial values; assert non-empty before use","Use the canonical feed URL 'https://www.python.org/jobs/feed/rss/' rather than hand-assembling paths"],"tags":["url-validation","configuration","pythonorg"],"backgroundTag":"invalid-url","analyzedSha":"e7abd431fce9348a95261acac9e0c14779c35df8","analyzedAt":"2026-09-22T13:19:01.448Z","contentChangedAt":"2026-09-22T13:19:01.448Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}