n8n-io/n8n · error · UserError

MCP server "${server.name}": invalid URL "${server.url}"

Error message

MCP server "${server.name}": invalid URL "${server.url}"

What it means

Thrown by McpClientManager.validateConfigs when a configured MCP server has a non-empty url that cannot be parsed by the URL constructor. This is a UserError — the user supplied an invalid configuration. The check is the first of three (parseability, http(s) protocol, SSRF safety); only the parse step throws this specific message.

Source

Thrown at packages/@n8n/instance-ai/src/mcp/mcp-client-manager.ts:232

		})();

		inFlight.set(key, promise);
		try {
			return await promise;
		} finally {
			inFlight.delete(key);
		}
	}

	private async validateConfigs(configs: McpServerConfig[]): Promise<void> {
		for (const server of configs) {
			if (!server.url) continue;

			let parsed: URL;
			try {
				parsed = new URL(server.url);
			} catch {
				throw new UserError(`MCP server "${server.name}": invalid URL "${server.url}"`);
			}

			if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
				throw new UserError(
					`MCP server "${server.name}": only http(s) URLs are allowed, got "${parsed.protocol}"`,
				);
			}

			if (this.ssrfValidator) {
				const result = await this.ssrfValidator.validateUrl(server.url);
				if (!result.ok) {
					throw new UserError(
						`MCP server "${server.name}": URL blocked by SSRF policy - ${result.error.message}`,
					);
				}
			}
		}
	}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Provide an absolute URL with an explicit scheme, e.g. 'https://mcp.example.com/sse'.
  2. If using a local server, prefix 'http://' (e.g. 'http://localhost:3000').
  3. Validate the URL string with `new URL(server.url)` in a scratch script to reproduce the exact parse failure.

Example fix

// before — missing scheme
{ name: 'my-mcp', url: 'mcp.local:3000/sse' }

// after — absolute http(s) URL
{ name: 'my-mcp', url: 'http://mcp.local:3000/sse' }
Defensive patterns

Strategy: validation

Validate before calling

function isValidMcpUrl(url: string): boolean {
  try {
    const parsed = new URL(url);
    return parsed.protocol === 'http:' || parsed.protocol === 'https:';
  } catch {
    return false;
  }
}

for (const server of configs) {
  if (server.url && !isValidMcpUrl(server.url)) {
    throw new Error(`server ${server.name} has an invalid MCP url: ${server.url}`);
  }
}

Type guard

function isParseableHttpUrl(url: string): boolean {
  try {
    const p = new URL(url);
    return p.protocol === 'http:' || p.protocol === 'https:';
  } catch {
    return false;
  }
}

Try / catch

import { UserError } from 'n8n-workflow';

try {
  await manager.validateConfigs(configs);
} catch (e) {
  if (e instanceof UserError && /invalid URL/.test(e.message)) {
    // surface the offending server config to the user; do not retry as-is
  }
  throw e;
}

Prevention

When it happens

Trigger: server.url is a relative path, a bare hostname without scheme, contains illegal characters, has malformed port, or is otherwise not a valid absolute URL string.

Common situations: User enters 'localhost:3000' (missing scheme) instead of 'http://localhost:3000'; a trailing slash in an unexpected position; copy-paste introduced a stray character; env-var templating left a placeholder unresolved.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/9b4dd01f67dd8ed1. Report an issue: GitHub.