{"record":{"id":"1a4e8abf277da2b8","repo":"abhigyanpatwari/GitNexus","slug":"llm-base-url-must-use-http-or-https-got-p","errorCode":null,"errorMessage":"LLM base URL must use http:// or https:// (got ${parsed.protocol})","messagePattern":"LLM base URL must use http:// or https:// \\(got (.+?)\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"gitnexus/src/core/wiki/llm-client.ts","lineNumber":248,"sourceCode":" * 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})`,\n      );\n    }\n  }\n}\n","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/wiki/llm-client.ts#L230-L266","documentation":"Thrown by `validateLLMBaseUrl` when the URL parses successfully but its protocol is neither `http:` nor `https:`. This blocks SSRF vectors via `file://`, `data:`, `javascript:`, `ftp:`, and other schemes that a misconfigured or malicious base URL could introduce. Only the parsed protocol (scheme) is included in the message, never the full URL, to avoid leaking credentials.","triggerScenarios":"`validateLLMBaseUrl` successfully constructs a `URL` object, but `parsed.protocol` is not `http:` or `https:` (e.g., `file:`, `data:`, `ftp:`, `javascript:`).","commonSituations":"`GITNEXUS_LLM_BASE_URL` set to a `file://` path (perhaps from a local-file-only configuration); a copy-paste from a documentation example that used a placeholder scheme; a misconfigured proxy or gateway URL that was entered with an internal protocol.","solutions":["Change the URL scheme to `https://` (preferred for all remote LLM endpoints).","For local servers (Ollama, LiteLLM, etc.), use `http://localhost:PORT` or `http://127.0.0.1:PORT` — these are allowed without an explicit insecure-connection entry.","For a LAN/self-hosted endpoint over plain HTTP, use `http://` and add the host to `GITNEXUS_ALLOW_INSECURE_CONNECTION`."],"exampleFix":"# before\nexport GITNEXUS_LLM_BASE_URL=file:///path/to/local/model\ngitnexus wiki\n# error: LLM base URL must use http:// or https:// (got file:)\n# after\nexport GITNEXUS_LLM_BASE_URL=http://localhost:11434  # local Ollama\ngitnexus wiki","handlingStrategy":"validation","validationCode":"// Validate the protocol 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('LLM base URL validation failed:', (err as Error).message);\n  process.exit(1);\n}","typeGuard":"const isHttpOrHttpsUrl = (url: string): boolean => {\n  try {\n    const parsed = new URL(url);\n    return parsed.protocol === 'http:' || parsed.protocol === 'https:';\n  } catch {\n    return false;\n  }\n};","tryCatchPattern":"try {\n  validateLLMBaseUrl(baseUrl);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('must use http:// or https://')) {\n    console.error('Change the URL scheme to http:// or https://.');\n  }\n  throw err;\n}","preventionTips":["Use `https://` for all remote LLM endpoints.","For local servers, use `http://localhost:PORT` or `http://127.0.0.1:PORT` — no allowlist needed.","Never use `file://`, `data:`, or other non-HTTP schemes for LLM base URLs."],"tags":["config","security","llm-client","validation","url","ssrf"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}