decolua/9router · error

Invalid URL format

Error message

Invalid URL format

What it means

After confirming a `url` string was provided, handleFetch validates it with the WHATWG URL parser (new URL(targetUrl)). A SyntaxError - meaning the string is not an absolute URL with a scheme like http:// or https:// - produces HTTP 400 'Invalid URL format'. Relative paths, bare hostnames, and malformed URLs are all rejected here.

Source

Thrown at src/sse/handlers/fetch.js:78

      return errorResponse(HTTP_STATUS.UNAUTHORIZED, "Invalid API key");
    }
  }

  if (!providerInput || typeof providerInput !== "string") {
    log.warn("FETCH", "Missing provider/model");
    return errorResponse(HTTP_STATUS.BAD_REQUEST, "Missing required field: provider (or model)");
  }

  if (!targetUrl || typeof targetUrl !== "string") {
    log.warn("FETCH", "Missing url");
    return errorResponse(HTTP_STATUS.BAD_REQUEST, "Missing required field: url");
  }

  // Validate URL format
  try {
    new URL(targetUrl);
  } catch {
    log.warn("FETCH", "Invalid URL", { url: targetUrl });
    return errorResponse(HTTP_STATUS.BAD_REQUEST, "Invalid URL format");
  }

  // SSRF guard: reject internal/private/metadata targets
  try {
    assertPublicUrl(targetUrl);
  } catch (err) {
    log.warn("FETCH", "Blocked URL", { url: targetUrl });
    return errorResponse(HTTP_STATUS.BAD_REQUEST, err.message);
  }

  // Combo expansion: providerInput may be a combo name → run fallback/round-robin across providers
  const combos = await getCombos();
  const comboModels = getComboModelsFromData(providerInput, combos);
  if (comboModels) {
    const comboStrategies = settings.comboStrategies || {};
    const comboStrategy = comboStrategies[providerInput]?.fallbackStrategy || settings.comboStrategy || "fallback";
    const comboStickyLimit = settings.comboStickyRoundRobinLimit;

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Prefix the scheme if missing: 'https://' + host when the input is a bare hostname
  2. Trim whitespace and strip wrapping quotes/angle brackets from the URL string before sending
  3. Pre-validate client-side with new URL(value) inside try/catch to catch it before the request
  4. URL-encode unsafe characters (spaces, unencoded non-ASCII) in query/path segments

Example fix

// before
const url = 'example.com/docs';
// after
const raw = 'example.com/docs'.trim();
const url = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(raw) ? raw : 'https://' + raw;
Defensive patterns

Strategy: validation

Validate before calling

function assertAbsoluteUrl(value) {
  if (typeof value !== 'string') throw new TypeError('url must be a string');
  const u = new URL(value); // throws SyntaxError on invalid input
  if (!['http:', 'https:'].includes(u.protocol)) throw new TypeError('url must be http(s)');
  return u.href;
}
const safeUrl = assertAbsoluteUrl(input.trim());

Type guard

function isParseableUrl(value) {
  if (typeof value !== 'string') return false;
  try { const u = new URL(value); return u.protocol === 'http:' || u.protocol === 'https:'; } catch { return false; }
}

Try / catch

try {
  const res = await fetch(endpoint, { method: 'POST', body: JSON.stringify({ model, url }) });
  if (res.status === 400 && (await res.text()).includes('Invalid URL format')) {
    console.error('Not an absolute URL:', url);
  }
} catch (err) { /* network failure */ }

Prevention

When it happens

Trigger: Sending url='example.com/page' (no scheme), url='/relative/path', url='http://' or 'https://[bad' (malformed), or any string the URL constructor cannot parse as absolute.

Common situations: Passing a hostname without protocol because browsers auto-prefix http://; user-typed input pasted with typos or spaces; template interpolation producing 'https://{missing}'; URLs truncated by a length-limited form field.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/f7228852f79eb877. Report an issue: GitHub.