unslothai/unsloth · warning · HTTPException

url must not be empty

Error message

url must not be empty

What it means

400 from _validate_url when the MCP server 'url' field is empty or only whitespace after stripping. The field is overloaded: it holds either an http(s) URL or (when stdio is enabled) a local command, but it may not be blank in either mode.

Source

Thrown at studio/backend/routes/mcp_servers.py:65

router = APIRouter()

# Only a UI session may define a local command; API keys keep http(s) MCP.
# Annotated, not a Depends default: these routes are also called directly by the
# tests, where a Depends object is truthy and would read as "API key".
ViaApiKey = Annotated[bool, Depends(authenticated_via_api_key)]


def _looks_like_command(value: str) -> bool:
    """Whitespace is a one-way signal: a URL can't hold an unencoded space, so
    a value with whitespace is definitely a command. No whitespace proves
    nothing (a lone token may be a single-arg command or a scheme-less URL)."""
    return any(ch.isspace() for ch in value)


def _validate_url(url: str) -> str:
    trimmed = (url or "").strip()
    if not trimmed:
        raise HTTPException(status_code = 400, detail = "url must not be empty")
    # When stdio is enabled, a non-HTTP value is a local command (reuses this
    # field so stdio servers ride existing CRUD/storage).
    if stdio_mcp_enabled() and is_stdio(trimmed):
        try:
            parts = parse_stdio_command(trimmed)
        except ValueError as exc:
            raise log_and_http_error(
                exc,
                400,
                "Invalid command. Check quoting and try again.",
                event = "mcp_servers.invalid_command",
                log = logger,
            )
        if not parts or not parts[0].strip():
            raise HTTPException(status_code = 400, detail = "command must not be empty")
        if "://" in parts[0]:
            # A URL-scheme first token is a mistyped URL, not a command. Reject
            # cleanly instead of exec-ing it (mirrors the frontend check).

View on GitHub (pinned to 203007d190)

Solutions

  1. Provide a non-blank url: an http(s):// URL or, when stdio is enabled, a local command string.
  2. Trim input client-side and disable submit while the field is empty.
  3. For updates, omit the 'url' key entirely instead of sending an empty string when you don't intend to change it.

Example fix

// before
await api.post('/api/mcp-servers/', {display_name: 'fs', url: ''});

// after
const url = urlInput.trim();
if (!url) throw new Error('URL is required');
await api.post('/api/mcp-servers/', {display_name: 'fs', url});
Defensive patterns

Strategy: validation

Validate before calling

const url = (payload.url ?? '').trim();
if (!url) throw new Error('MCP url is required');

Type guard

function hasNonEmptyUrl(p) { return typeof p.url === 'string' && p.url.trim().length > 0; }

Prevention

When it happens

Trigger: POST /api/mcp-servers/ (create) or PUT /{server_id} with url set to '', ' ', or null coerced to empty; also PUT that includes 'url' in the payload with an empty value.

Common situations: Frontend form submitted before the user typed an address, JSON built with a default empty string, or an update payload that accidentally includes url:'' alongside other fields.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/62fbaf222d433c13. Report an issue: GitHub.