{"record":{"id":"00f65e302501e9ed","repo":"santifer/career-ops","slug":"glints-invalid-url-url","errorCode":null,"errorMessage":"glints: invalid URL: ${url}","messagePattern":"glints: invalid URL: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/glints.mjs","lineNumber":75,"sourceCode":"        salaryMode\n        maxAmount\n        minAmount\n        CurrencyCode\n      }\n      createdAt\n    }\n    expInfo\n    hasMore\n  }\n}`;\n\n/** @param {string} url */\nfunction assertGlintsUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`glints: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`glints: URL must use HTTPS: ${url}`);\n  if (!ALLOWED_GLINTS_HOSTS.has(parsed.hostname))\n    throw new Error(`glints: untrusted hostname \"${parsed.hostname}\" — must be one of: ${[...ALLOWED_GLINTS_HOSTS].join(', ')}`);\n  return url;\n}\n\n// NaN-safe Date.parse\nfunction toEpochMs(value) {\n  if (!value) return undefined;\n  const parsed = Date.parse(value);\n  return Number.isNaN(parsed) ? undefined : parsed;\n}\n\n/**\n * Derive the job detail base URL from the API hostname.\n * @param {string} apiUrl\n * @returns {string}","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/glints.mjs#L57-L93","documentation":"glints.mjs throws this inside assertGlintsUrl() when `new URL(url)` raises — the string is not an absolute, parseable URL. Unlike the constant-pinned providers, Glints validates entry.api (the operator-overridable GraphQL endpoint) falling back to DEFAULT_API, so a live throw most often comes from a malformed api: value in portals.yml.","triggerScenarios":"A portals.yml entry sets api to a relative path, a string with spaces/quotes, an empty string, or undefined; api was built from an env var that resolved to a non-URL; a test calls assertGlintsUrl('/api/v2-alc/graphql').","commonSituations":"Operator points Glints at a custom endpoint but types a path-only value; a templated/CI-generated portals.yml left api blank or malformed; an env override injected a bad value.","solutions":["Set api to a full absolute URL (e.g. https://glints.com/api/v2-alc/graphql) or remove the api field to use the default.","Check the YAML around the api: line for stray quotes, spaces, or an unclosed string.","If the value comes from an env var, confirm it resolves to a real URL in the target environment."],"exampleFix":"# before\n- name: Glints (ID)\n  provider: glints\n  api: /api/v2-alc/graphql   # relative -> throws\n\n# after\n- name: Glints (ID)\n  provider: glints\n  api: https://glints.com/api/v2-alc/graphql","handlingStrategy":"validation","validationCode":"// Pre-flight: validate a Glints entry.api (or the default) before fetching.\nfunction glintsApiIsValid(entry) {\n  const api = entry.api || 'https://glints.com/api/v2-alc/graphql';\n  let u;\n  try { u = new URL(api); } catch { return false; }\n  return u.protocol === 'https:';\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer omitting api to use the maintained default; only set it when you have a verified endpoint.","Add a portals.yml lint pass that calls glintsApiIsValid on every provider: glints entry.","Render portals.yml through a validator that rejects non-URL api values."],"tags":["url-validation","glints","config","portals-yml"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}