ruvnet/ruflo · error · HttpFetchValidationError

INVALID_URL

INVALID_URL

Error message

invalid URL: ${rawUrl}

What it means

Thrown by validateUrl in the http_fetch MCP tool when the native URL constructor cannot parse the input string. This is the first validation gate of the secure-by-default fetch pipeline (ADR-164 §5.1.8); it rejects malformed URLs before any protocol or host checks run. The error carries code INVALID_URL.

Source

Thrown at v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts:51

export class HttpFetchValidationError extends Error {
  constructor(message: string, public readonly code: string) {
    super(message);
    this.name = 'HttpFetchValidationError';
  }
}

/**
 * Decide whether the URL is permitted under the default secure-by-default
 * allowlist. Block file://, ftp://, RFC-1918 private addresses, loopback,
 * link-local — unless CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 is set.
 */
export function validateUrl(rawUrl: string): URL {
  let parsed: URL;
  try {
    parsed = new URL(rawUrl);
  } catch {
    throw new HttpFetchValidationError(`invalid URL: ${rawUrl}`, 'INVALID_URL');
  }
  const proto = parsed.protocol.toLowerCase();
  if (proto !== 'http:' && proto !== 'https:') {
    throw new HttpFetchValidationError(
      `protocol ${parsed.protocol} not allowed (only http: and https:)`,
      'FORBIDDEN_PROTOCOL',
    );
  }
  const host = parsed.hostname.toLowerCase();
  const allowPrivate = process.env.CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE === '1';
  if (!allowPrivate && isPrivateOrLoopback(host)) {
    throw new HttpFetchValidationError(
      `host ${host} is loopback/private/link-local; set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 to override`,
      'PRIVATE_ADDRESS',
    );
  }
  return parsed;
}

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Provide an absolute URL with an explicit scheme: 'https://example.com/path'.
  2. URL-encode query parameters with encodeURIComponent before concatenating.
  3. Trim whitespace and surrounding angle brackets from pasted URLs.
  4. Pre-validate with `new URL(url)` in a try/catch before calling http_fetch.

Example fix

// before
http_fetch({ url: 'example.com/api?q=a b' })
// after
http_fetch({ url: 'https://example.com/api?q=' + encodeURIComponent('a b') })
Defensive patterns

Strategy: validation

Validate before calling

function safeParseUrl(raw) {
  try { return new URL(raw); }
  catch { throw new Error(`invalid URL: ${raw}`); }
}

Type guard

function isValidUrl(s: unknown): s is string {
  if (typeof s !== 'string') return false;
  try { new URL(s); return true; } catch { return false; }
}

Prevention

When it happens

Trigger: Passing a URL string that fails `new URL(rawUrl)` — empty string, missing scheme ('example.com'), unencoded spaces, stray characters, or a non-string value coerced to string. The check runs inside the validateUrl helper which the http_fetch handler calls.

Common situations: Omitting the https:// scheme; pasting a URL with spaces or angle brackets; building a URL from unencoded user input with query parameters; passing a relative path instead of an absolute URL.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/7aba912ba532475b. Report an issue: GitHub.