abhigyanpatwari/GitNexus · error · Error
Invalid LLM base URL: must be a well-formed http:// or…
Error message
Invalid LLM base URL: must be a well-formed http:// or https:// URL
What it means
validateLLMBaseUrl parses the value supplied via `--base-url` (or provider config) with `new URL()` and throws when parsing fails, i.e. the string is not an absolute well-formed URL. The message deliberately omits the raw input because base URLs frequently embed API keys as credentials; only the failure class is reported. This is the first of three validation stages (parse, scheme, insecure-host).
Solutions
- Supply an absolute URL with scheme: `--base-url https://api.example.com/v1`
- Trim whitespace/newlines if the value comes from an env var or script variable
- If the value intentionally contains credentials (user:pass@host), keep the scheme and move the key to `--api-key` instead
Example fix
# before gitnexus wiki --provider custom --base-url api.example.com/v1 # after gitnexus wiki --provider custom --base-url https://api.example.com/v1
Defensive patterns
Strategy: validation
Validate before calling
function isParsableAbsoluteUrl(value: string): boolean {
try { new URL(value); return true; } catch { return false; }
}
if (!isParsableAbsoluteUrl(baseUrl)) throw new Error('baseUrl must be an absolute http(s) URL'); Type guard
function isParsableAbsoluteUrl(value: string): boolean {
try { new URL(value); return true; } catch { return false; }
} Try / catch
try {
validateLLMBaseUrl(baseUrl);
} catch (err) {
if (err instanceof Error && err.message.includes('well-formed http:// or https://')) {
// likely missing scheme — prepend https:// and re-validate once
}
} Prevention
- Validate base URLs with `new URL()` at config-load time, not at request time
- Normalize env-provided URLs (trim whitespace/newlines) before use
- Fail fast in setup wizards: test the URL before persisting provider config
When it happens
Trigger: Passing a bare host or host:port (`--base-url api.example.com/v1`), a relative path (`/v1`), a string with spaces or unmatched brackets, or a fully empty value; programmatically passing a variable that is undefined-interpolated into the URL string.
Common situations: Users omitting the https:// scheme; copy-pasting from docs that strip the scheme; trailing whitespace/newline in the value from a shell variable or .env; wrong env var referenced so the value is literally 'undefined'.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- --allow-insecure-connection /…
- Insecure http:// LLM base URLs are only allowed for…
- LLM base URL must use http:// or https://
- LLM request timed out after
- Azure content filter blocked this request. The prompt…
AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-08-20).
Data as JSON: /api/errors/1b5b152594f21e44.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/wiki/llm-client.ts:247
* - file://, data:, javascript:, and any other non-HTTP scheme
* - http:// aimed at non-loopback hosts unless explicitly allowlisted
* (avoids SSRF against internal networks by default)
*
* Throws with a descriptive message on validation failure so callers surface a
* clear error rather than an opaque network error.
*/
export function validateLLMBaseUrl(
baseUrl: string,
allowedInsecureHttpHosts: readonly string[] = parseLLMAllowedInsecureHttpHosts(
process.env[LLM_ALLOW_INSECURE_CONNECTION_ENV],
),
): void {
let parsed: URL;
try {
parsed = new URL(baseUrl);
} catch {
// Do not include the raw input in the message — it may contain credentials.
throw new Error('Invalid LLM base URL: must be a well-formed http:// or https:// URL');
}
if (!['https:', 'http:'].includes(parsed.protocol)) {
// Use parsed.protocol only (scheme), not the full URL, to avoid leaking credentials.
throw new Error(`LLM base URL must use http:// or https:// (got ${parsed.protocol})`);
}
if (parsed.protocol === 'http:') {
// Node's URL parser preserves IPv6 brackets in hostname (e.g. "[::1]"),
// so strip them before comparing to bare address literals.
const host = parsed.hostname.toLowerCase().replace(/^\[|\]$/g, '');
const allowedHosts = new Set(allowedInsecureHttpHosts.map(normalizeAllowedInsecureHttpHost));
if (host !== 'localhost' && host !== '127.0.0.1' && host !== '::1' && !allowedHosts.has(host)) {
// Use parsed.origin (scheme+host+port, no credentials) instead of the full URL.
throw new Error(
`Insecure http:// LLM base URLs are only allowed for localhost/127.0.0.1 ` +
`or hosts listed by --allow-insecure-connection / ${LLM_ALLOW_INSECURE_CONNECTION_ENV}. ` +
`Use https:// for remote endpoints (got ${parsed.origin})`,View on GitHub (pinned to 52924ef12c)