{"record":{"id":"b9b00543349d5c8f","repo":"santifer/career-ops","slug":"nofluffjobs-invalid-url-url","errorCode":null,"errorMessage":"nofluffjobs: invalid URL: ${url}","messagePattern":"nofluffjobs: invalid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/nofluffjobs.mjs","lineNumber":19,"sourceCode":"// @ts-check\n/** @typedef {import('./_types.js').Provider} Provider */\n\n// NoFluffJobs provider — hits the public search posting API.\n// It intentionally returns only the core scanner job fields; richer skill and\n// salary metadata can be added later if the provider contract is expanded.\n\nconst ALLOWED_HOSTS = new Set(['nofluffjobs.com']);\nconst API_URL = 'https://nofluffjobs.com/api/search/posting';\nconst JOB_BASE = 'https://nofluffjobs.com/pl/job/';\nconst PAGE_SIZE = 20;\nconst MAX_PAGES = 5;\n\nfunction assertNoFluffUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`nofluffjobs: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`nofluffjobs: URL must use HTTPS: ${url}`);\n  if (!ALLOWED_HOSTS.has(parsed.hostname)) {\n    throw new Error(`nofluffjobs: untrusted hostname \"${parsed.hostname}\" — must be nofluffjobs.com`);\n  }\n  return parsed;\n}\n\nfunction detectUrl(entry) {\n  const url = entry.api || entry.careers_url || '';\n  if (typeof url !== 'string' || !url.trim()) return null;\n  try {\n    return { url: assertNoFluffUrl(url).href };\n  } catch {\n    return null;\n  }\n}\n","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/nofluffjobs.mjs#L1-L37","documentation":"The nofluffjobs provider validates URLs with assertNoFluffUrl before use. This error is thrown when the input string fails the URL constructor — it is not a syntactically valid absolute URL. Like the other providers, it fails fast before any network activity.","triggerScenarios":"detectUrl/fetch handing assertNoFluffUrl a careers_url or api value that is empty, scheme-less ('nofluffjobs.com/api'), malformed ('https://', 'https:/x'), or contains whitespace/invalid characters.","commonSituations":"portals.yml entry with a missing or truncated URL; unset variable interpolated into the config; YAML line-wrapping or quoting mistakes splitting the URL; placeholder text left in a copied template entry.","solutions":["Repair the URL so it is a full absolute https:// URL","Check the portals.yml entry for quoting/indentation issues that truncate the value","Ensure the variable feeding the URL is actually set and trimmed","Remove entries that are not meant to use the NoFluffJobs provider"],"exampleFix":"// before\nconst url = entry.careers_url || ''; // '' -> throws\nassertNoFluffUrl(url);\n// after\nconst url = (entry.api || entry.careers_url || '').trim();\nif (!url) return null; // entry is not for this provider\nassertNoFluffUrl(url);","handlingStrategy":"validation","validationCode":"function isParsableUrl(u) { try { new URL(u); return true; } catch { return false; } }\nif (!isParsableUrl(entry.careers_url)) throw new Error(`bad nofluffjobs url: ${entry.careers_url}`);","typeGuard":"function isAbsoluteUrl(u) {\n  if (typeof u !== 'string' || !u.trim()) return false;\n  try { const p = new URL(u); return !!p.hostname; } catch { return false; }\n}","tryCatchPattern":"try {\n  await nofluffjobs.fetch(entry, ctx);\n} catch (e) {\n  if (String(e.message).startsWith('nofluffjobs: invalid URL')) {\n    log.warn(`Fix portals.yml entry for ${entry.company}: ${e.message}`);\n    return null;\n  }\n  throw e;\n}","preventionTips":["Validate every portals.yml URL at startup with a dry parse","Quote YAML values containing special characters so they are not truncated or mangled","Check that the source field is set before interpolating it into a URL","Prefer copy-paste of full URLs over retyping them"],"tags":["url-validation","config","input-validation"],"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"}