{"record":{"id":"1b5b152594f21e44","repo":"abhigyanpatwari/GitNexus","slug":"invalid-llm-base-url-must-be-a-well-formed-http","errorCode":null,"errorMessage":"Invalid LLM base URL: must be a well-formed http:// or https:// URL","messagePattern":"Invalid LLM base URL: must be a well-formed http:// or https:// URL","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"gitnexus/src/core/wiki/llm-client.ts","lineNumber":243,"sourceCode":" *  - file://, data:, javascript:, and any other non-HTTP scheme\n *  - http:// aimed at non-loopback hosts unless explicitly allowlisted\n *    (avoids SSRF against internal networks by default)\n *\n * Throws with a descriptive message on validation failure so callers surface a\n * clear error rather than an opaque network error.\n */\nexport function validateLLMBaseUrl(\n  baseUrl: string,\n  allowedInsecureHttpHosts: readonly string[] = parseLLMAllowedInsecureHttpHosts(\n    process.env[LLM_ALLOW_INSECURE_CONNECTION_ENV],\n  ),\n): void {\n  let parsed: URL;\n  try {\n    parsed = new URL(baseUrl);\n  } catch {\n    // Do not include the raw input in the message — it may contain credentials.\n    throw new Error('Invalid LLM base URL: must be a well-formed http:// or https:// URL');\n  }\n\n  if (!['https:', 'http:'].includes(parsed.protocol)) {\n    // Use parsed.protocol only (scheme), not the full URL, to avoid leaking credentials.\n    throw new Error(`LLM base URL must use http:// or https:// (got ${parsed.protocol})`);\n  }\n\n  if (parsed.protocol === 'http:') {\n    // Node's URL parser preserves IPv6 brackets in hostname (e.g. \"[::1]\"),\n    // so strip them before comparing to bare address literals.\n    const host = parsed.hostname.toLowerCase().replace(/^\\[|\\]$/g, '');\n    const allowedHosts = new Set(allowedInsecureHttpHosts.map(normalizeAllowedInsecureHttpHost));\n    if (host !== 'localhost' && host !== '127.0.0.1' && host !== '::1' && !allowedHosts.has(host)) {\n      // Use parsed.origin (scheme+host+port, no credentials) instead of the full URL.\n      throw new Error(\n        `Insecure http:// LLM base URLs are only allowed for localhost/127.0.0.1 ` +\n          `or hosts listed by --allow-insecure-connection / ${LLM_ALLOW_INSECURE_CONNECTION_ENV}. ` +\n          `Use https:// for remote endpoints (got ${parsed.origin})`,","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/wiki/llm-client.ts#L225-L261","documentation":"Thrown by `validateLLMBaseUrl` when `new URL(baseUrl)` throws — the supplied LLM base URL is not a parseable URL at all. The raw input is deliberately excluded from the error message because it may contain embedded credentials (e.g., `https://user:pass@host`), and echoing it would leak secrets into logs or error reports.","triggerScenarios":"`validateLLMBaseUrl(baseUrl)` is called (at LLM client initialization or wiki generation), and `new URL(baseUrl)` throws a TypeError. The input is structurally invalid as a URL: missing protocol, unencoded spaces, malformed authority, etc.","commonSituations":"`GITNEXUS_LLM_BASE_URL` is set without a protocol (`api.example.com/v1`); contains unencoded spaces; has a typo in the scheme (`htp://`); is an empty string; or was constructed by string concatenation that produced an invalid URL.","solutions":["Ensure the base URL includes a valid `http://` or `https://` scheme (https is preferred for remote endpoints).","Verify there are no unencoded special characters or spaces in the URL.","If the URL contains credentials, use standard `user:pass@host` URL encoding — the validation accepts it but never echoes it back.","Test the URL with `new URL(yourUrl)` in a Node REPL to confirm it parses."],"exampleFix":"# before\nexport GITNEXUS_LLM_BASE_URL=api.openai.com/v1\ngitnexus wiki\n# error: Invalid LLM base URL: must be a well-formed http:// or https:// URL\n# after\nexport GITNEXUS_LLM_BASE_URL=https://api.openai.com/v1\ngitnexus wiki","handlingStrategy":"validation","validationCode":"// Validate the base URL before initializing the LLM client:\nimport { validateLLMBaseUrl } from './wiki/llm-client.js';\ntry {\n  validateLLMBaseUrl(process.env.GITNEXUS_LLM_BASE_URL!);\n} catch (err) {\n  console.error('Invalid LLM base URL:', (err as Error).message);\n  process.exit(1);\n}","typeGuard":"const isValidBaseUrl = (url: string): boolean => {\n  try { new URL(url); return true; } catch { return false; }\n};","tryCatchPattern":"try {\n  validateLLMBaseUrl(baseUrl);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('well-formed http:// or https:// URL')) {\n    console.error('Ensure GITNEXUS_LLM_BASE_URL includes the scheme (https://...).');\n  }\n  throw err;\n}","preventionTips":["Always include the scheme in `GITNEXUS_LLM_BASE_URL` (`https://api.openai.com/v1`).","Never embed credentials in the URL string visible to logs — use headers if possible.","Test the URL with `new URL(url)` in a Node REPL before setting it as an env var."],"tags":["config","security","llm-client","validation","url","env-var"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}