langflow-ai/langflow · error · ValueError

Unsupported client

Error message

Unsupported client

What it means

Raised by get_config_path() in Langflow's MCP projects API when the client argument does not match one of the three supported names. The function only handles client.lower() == 'cursor', 'windsurf', or 'claude'; any other string (including typos, new clients, or different casing/whitespace) falls through to the terminal 'Unsupported client' ValueError at mcp_projects.py:1291.

Source

Thrown at src/backend/base/langflow/api/v1/mcp_projects.py:1291

                    if not Path("/mnt/c").exists():
                        msg = "Windows C: drive not mounted at /mnt/c in WSL"
                        raise ValueError(msg)

                    msg = "Could not find valid Windows user directory in WSL"
                    raise ValueError(msg)
                except (OSError, CalledProcessError) as e:
                    await logger.awarning("Failed to determine Windows user path in WSL: %s", str(e))
                    msg = f"Could not determine Windows Claude config path in WSL: {e!s}"
                    raise ValueError(msg) from e
            # Regular Windows
            return Path(os.environ["APPDATA"]) / "Claude" / "claude_desktop_config.json"

        msg = "Unsupported operating system for Claude configuration"
        raise ValueError(msg)

    msg = "Unsupported client"
    raise ValueError(msg)


def remove_server_by_urls(config_data: dict, urls: Sequence[str] | str) -> tuple[dict, list[str]]:
    """Remove any MCP servers that use one of the specified URLs from config data.

    Returns:
        tuple: (updated_config, list_of_removed_server_names)
    """
    normalized_urls = _normalize_url_list(urls)
    if not normalized_urls:
        return config_data, []

    if "mcpServers" not in config_data:
        return config_data, []

    removed_servers: list[str] = []
    servers_to_remove: list[str] = []

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Pass one of the supported machine names exactly: 'cursor', 'windsurf', or 'claude' (any casing, but no extra whitespace).
  2. Normalize user input before calling: client.strip().lower() and reject values outside {'cursor','windsurf','claude'} with a clear validation message.
  3. To support a new client, extend get_config_path with a new branch returning its config path and update the client list used by the availability-check endpoints so it stays consistent.

Example fix

# before
client = request_client_name  # e.g. 'Claude Desktop ' -> raises
path = await get_config_path(client)

# after
SUPPORTED_CLIENTS = {"cursor", "windsurf", "claude"}
client = request_client_name.strip().lower()
if client not in SUPPORTED_CLIENTS:
    msg = f"Unsupported client {request_client_name!r}; expected one of {sorted(SUPPORTED_CLIENTS)}"
    raise ValueError(msg)
path = await get_config_path(client)
Defensive patterns

Strategy: type-guard

Validate before calling

SUPPORTED_MCP_CLIENTS = {"cursor", "windsurf", "claude"}

def is_supported_client(client: str) -> bool:
    return isinstance(client, str) and client.strip().lower() in SUPPORTED_MCP_CLIENTS

# before calling:
if not is_supported_client(client):
    raise HTTPException(400, f"Unsupported client; choose from {sorted(SUPPORTED_MCP_CLIENTS)}")

Type guard

from typing import Literal

ClientName = Literal["cursor", "windsurf", "claude"]

def is_client_name(value: str) -> bool:
    """Narrow an arbitrary string to the supported client union."""
    return isinstance(value, str) and value.strip().lower() in {"cursor", "windsurf", "claude"}

Try / catch

try:
    path = await get_config_path(client)
except ValueError as e:
    if "Unsupported client" in str(e):
        raise HTTPException(status_code=400, detail=str(e)) from e
    raise

Prevention

When it happens

Trigger: Calling get_config_path('Claude Code'), 'vscode', 'cline', 'codex', or any client name other than exactly cursor/windsurf/claude (case-insensitive). Also triggered by strings with leading/trailing whitespace (' claude') or trailing newlines, since the comparison uses client.lower() without stripping.

Common situations: Frontend or API consumers passing a display name ('Claude Desktop') instead of the machine name 'claude'; adding support for a new MCP client in a fork without extending this function; user-supplied client strings from a form or config file that include whitespace or different casing.

Related errors


AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14). Data as JSON: /api/errors/f48d333fb6d70311. Report an issue: GitHub.