{"record":{"id":"c0cd18473b2098c3","repo":"santifer/career-ops","slug":"h1b-api-base-is-not-a-valid-url-trimmed","errorCode":null,"errorMessage":"H1B_API_BASE is not a valid URL: ${trimmed}","messagePattern":"H1B_API_BASE is not a valid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/h1b-sponsor/lib/api.mjs","lineNumber":50,"sourceCode":"}\n\nfunction resolveBase() {\n  const raw = process.env.H1B_API_BASE;\n  // Absent means \"use the default\". Present but blank is a misconfiguration\n  // (an unset shell variable, an empty .env line, a CI secret that did not\n  // populate), and silently falling back would send someone's shortlist and\n  // their token to a host they believed they had replaced.\n  if (raw === undefined) return DEFAULT_BASE;\n  const trimmed = String(raw).trim();\n  if (!trimmed) {\n    throw new Error('H1B_API_BASE is set but empty. Unset it to use the default endpoint.');\n  }\n\n  let parsed;\n  try {\n    parsed = new URL(trimmed);\n  } catch {\n    throw new Error(`H1B_API_BASE is not a valid URL: ${trimmed}`);\n  }\n  if (parsed.username || parsed.password) {\n    // Undici refuses a credentialed Request anyway, and the value reaches\n    // stdout through the source field, so this would print a password.\n    throw new Error('H1B_API_BASE must not embed credentials.');\n  }\n  if (parsed.search || parsed.hash) {\n    // Paths are appended, so a query or fragment swallows them: the request\n    // would go to the base itself and answer about a company never asked for.\n    throw new Error('H1B_API_BASE must not contain a query string or a fragment.');\n  }\n  // Plain http would put an Authorization header on the wire in the clear.\n  // Loopback is exempt so a self-hoster can develop against a local worker,\n  // but only for http. Exempting every scheme on a loopback host let\n  // ftp://localhost and ws://localhost past validation, and those die later\n  // inside fetch as a bare \"fetch failed\", which is the opaque failure this\n  // check exists to replace with a named configuration error.\n  const loopback = /^(localhost|127\\.\\d+\\.\\d+\\.\\d+|\\[::1\\]|::1)$/i.test(parsed.hostname);","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/plugins/h1b-sponsor/lib/api.mjs#L32-L68","documentation":"resolveBase() validates the H1B_API_BASE environment variable before any request is made. The value failed `new URL(trimmed)`, meaning it is not a syntactically valid absolute URL (missing scheme, spaces, typos like 'h1b.example.com/api' without protocol). The library throws early with a named configuration error instead of letting fetch fail opaquely later.","triggerScenarios":"Calling any API function (e.g. getEmployerProfile) when the H1B_API_BASE env var is set to a string that the URL constructor cannot parse — missing 'https://' scheme, containing spaces or invalid characters, or being a bare hostname/path.","commonSituations":"Copying an API host without the scheme into .env; wrapping the value in shell quotes that leak into the variable; typos like 'https:/example.com' (single slash); using a relative path like '/api' by mistake.","solutions":["Prefix the value with a full scheme, e.g. H1B_API_BASE=https://h1b.example.com in .env or the environment.","Check for stray characters: surrounding quotes, spaces, or a single slash after the scheme; fix and re-run.","If you meant to use the plugin's default API, unset H1B_API_BASE entirely instead of setting it to a partial value.","Validate locally before running: `new URL(process.env.H1B_API_BASE)` in node should not throw."],"exampleFix":"// before (.env)\nH1B_API_BASE=h1b-api.example.com/v1\n// after\nH1B_API_BASE=https://h1b-api.example.com/v1","handlingStrategy":"validation","validationCode":"function isValidApiBase(v) {\n  if (typeof v !== 'string' || !v.trim()) return false;\n  try { new URL(v.trim()); return true; } catch { return false; }\n}\n// before running: if (!isValidApiBase(process.env.H1B_API_BASE)) fix config;","typeGuard":"function isHttpUrl(v) {\n  try { const u = new URL(v); return u.protocol === 'https:' || u.protocol === 'http:'; }\n  catch { return false; }\n}","tryCatchPattern":"try {\n  const profile = await getEmployerProfile(id);\n} catch (e) {\n  if (e.message.startsWith('H1B_API_BASE is not a valid URL')) {\n    console.error('Fix H1B_API_BASE in .env — must be a full absolute URL, e.g. https://host');\n    process.exitCode = 2;\n    return;\n  }\n  throw e;\n}","preventionTips":["Always include the scheme (https://) when setting H1B_API_BASE.","Validate the env var at startup with new URL() before any work.","Keep .env values unquoted to avoid leaking shell quotes into the value.","Add a startup config lint that checks URL parseability, credentials, query/fragment, and scheme together."],"tags":["configuration","url-validation","env-var"],"backgroundTag":"invalid-base-url","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}