{"record":{"id":"9a43fd1b259276a1","repo":"santifer/career-ops","slug":"careerviet-invalid-url-url","errorCode":null,"errorMessage":"careerviet: invalid URL: ${url}","messagePattern":"careerviet: invalid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/careerviet.mjs","lineNumber":106,"sourceCode":" * The \"Cập nhật\" (updated) date sits in its own <li>, sibling to \"Hạn nộp\"\n * (deadline) inside the same .time block — the label text is what tells them\n * apart; both wrap their <time> the same way.\n */\nconst UPDATED_DATE_RE = /Cập nhật(?:<!--[\\s\\S]*?-->)?\\s*:?\\s*(?:<\\/span>)?\\s*<time>([\\d/-]+)<\\/time>/i;\n\n/** @param {any} ctx @param {number} ms */\nfunction sleep(ctx, ms) {\n  if (typeof ctx?.sleep === 'function') return ctx.sleep(ms);\n  return new Promise((r) => setTimeout(r, ms));\n}\n\n/** @param {string} url */\nfunction assertCareerVietUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`careerviet: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`careerviet: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`careerviet: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n/**\n * Collapse a markup fragment to its visible text.\n * @param {string} fragment\n * @returns {string}\n */\nexport function visibleText(fragment) {\n  return decodeEntities(\n    String(fragment ?? '')\n      .replace(/<!--[\\s\\S]*?-->/g, ' ')\n      .replace(/<[^>]+>/g, ' '),","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/careerviet.mjs#L88-L124","documentation":"The CareerViet provider is hard-pinned to a single trusted host, careerviet.vn (TRUSTED_HOST). assertCareerVietUrl first attempts new URL(url); a string that cannot parse as an absolute URL throws this error before any other validation. Unlike bamboohr/breezy which allow tenant subdomains, careerviet accepts only the exact apex host, but URL well-formedness is checked first.","triggerScenarios":"Calling the provider or assertCareerVietUrl with a non-URL string: a bare domain like 'careerviet.vn/viec-lam' without the scheme, an empty string, a string with spaces, or an interpolated variable that is undefined (producing 'undefined').","commonSituations":"YAML entry missing the https:// prefix; template string where the URL variable was never set; copy-paste from a document that stripped the scheme; building the URL from base + path where base itself was malformed.","solutions":["Make the value an absolute URL including the scheme: https://careerviet.vn/viec-lam","Trim whitespace and verify no invisible characters broke the URL string","Inspect the interpolated URL in the error message — 'undefined' or '' points to an unset variable upstream","In code, construct URLs via new URL(path, 'https://careerviet.vn') so well-formedness is guaranteed"],"exampleFix":"// before\ncareers_url: careerviet.vn/viec-lam\n// after\ncareers_url: https://careerviet.vn/viec-lam","handlingStrategy":"validation","validationCode":"function isParseableUrl(u) {\n  if (typeof u !== 'string' || !u.trim()) return false;\n  try { new URL(u); return true; } catch { return false; }\n}\nif (!isParseableUrl(entry.careers_url)) throw new Error(`bad careerviet URL for ${entry.name}: ${JSON.stringify(entry.careers_url)}`);","typeGuard":"function parseUrlOrNothing(value) {\n  if (typeof value !== 'string') return null;\n  try { return new URL(value); } catch { return null; }\n}","tryCatchPattern":"try {\n  await provider.fetch(entry, ctx);\n} catch (err) {\n  if (String(err.message).includes('invalid URL')) {\n    console.error(`Unparseable careerviet URL for ${entry.name} — add the https:// scheme and check variables`, err.message);\n  } else throw err;\n}","preventionTips":["Store absolute https:// URLs in entries — never bare domains or scheme-less strings","Check the failing string in the error message for 'undefined' or empty values from unset variables","Build page URLs with new URL(path, 'https://careerviet.vn') so they always parse","Trim whitespace and strip invisible characters from copy-pasted config values"],"tags":["url-validation","configuration","provider"],"backgroundTag":"invalid-url-format","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}