{"record":{"id":"6e13ada45a32e004","repo":"BerriAI/litellm","slug":"path-parameter-param-name-must-not-contain-pat","errorCode":null,"errorMessage":"Path parameter '{param_name}' must not contain path separators","messagePattern":"Path parameter '(.+?)' must not contain path separators","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/_experimental/mcp_server/openapi_to_mcp_generator.py","lineNumber":121,"sourceCode":"# Set from MCPServerManager.resolve_openapi_upstream_auth; authoritative\n# over every other Authorization source in _merge_openapi_tool_request_headers.\n_request_resolved_auth_headers: Final[contextvars.ContextVar[dict[str, str] | None]] = contextvars.ContextVar(\n    \"_request_resolved_auth_headers\", default=None\n)\n\n\ndef _sanitize_path_parameter_value(param_value: object, param_name: str) -> str:\n    \"\"\"Ensure path params cannot introduce directory traversal.\"\"\"\n    if param_value is None:\n        return \"\"\n\n    value_str: Final = str(param_value)\n    if value_str == \"\":\n        return \"\"\n\n    normalized_value: Final = value_str.replace(\"\\\\\", \"/\")\n    if \"/\" in normalized_value:\n        raise ValueError(f\"Path parameter '{param_name}' must not contain path separators\")\n\n    if any(part in {\".\", \"..\"} for part in PurePosixPath(normalized_value).parts):\n        raise ValueError(f\"Path parameter '{param_name}' cannot include '.' or '..' segments\")\n\n    return quote(value_str, safe=\"\")\n\n\ndef load_openapi_spec(filepath: str) -> dict[str, Any]:\n    \"\"\"\n    Sync wrapper. For URL specs, use the shared/custom MCP httpx client.\n    \"\"\"\n    try:\n        # If we're already inside an event loop, prefer the async function.\n        asyncio.get_running_loop()\n        raise RuntimeError(\n            \"load_openapi_spec() was called from within a running event loop. \"\n            \"Use 'await load_openapi_spec_async(...)' instead.\"\n        )","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/_experimental/mcp_server/openapi_to_mcp_generator.py#L103-L139","documentation":"Security guard in litellm's OpenAPI-to-MCP tool generator. When a generated MCP tool substitutes a caller-supplied value into a URL path segment, _sanitize_path_parameter_value rejects any value containing / (backslashes are normalized to / first) with this ValueError, blocking path traversal and URL-segment injection. On the generated tool's REST execution path the ValueError is caught and returned as the tool result string \"Invalid path parameter: ...\", so it usually surfaces as a failed tool result rather than an exception.","triggerScenarios":"Calling an MCP tool generated from an OpenAPI spec with a path parameter whose value contains / or \\, e.g. arguments={\"file_id\": \"docs/readme\"} for path /files/{file_id}; passing a Windows path (C:\\x\\y) or a value whose str() contains separators (a list ['a','b'] stringifies to a/b).","commonSituations":"Passing filenames with directories or Windows paths to REST-backed MCP tools; passing a full URL where only one segment was expected; passing non-string values (lists, objects) whose string form contains slashes; porting curl examples that embed slashes into tool args.","solutions":["Pass exactly one URL path segment per parameter - strip or restructure slashes before calling the tool.","If the argument came from user input or another file path, split it and use the real segment (or a tool whose OpenAPI path has one parameter per segment).","If the upstream API genuinely accepts an encoded slash inside one segment, verify against the generated tool's input schema; the generator percent-encodes the whole value (quote safe=\"\"), so pre-encoding a slash yourself results in double-encoding - use a slash-free identifier instead."],"exampleFix":"# before\nresult = await call_mcp_tool(server_id=\"myapi\", tool_name=\"get_file\", arguments={\"file_id\": \"docs/readme.md\"})\n# -> tool result: \"Invalid path parameter: Path parameter 'file_id' must not contain path separators\"\n\n# after\nresult = await call_mcp_tool(server_id=\"myapi\", tool_name=\"get_file\", arguments={\"file_id\": \"readme.md\"})","handlingStrategy":"validation","validationCode":"def is_single_path_segment(value: object) -> bool:\n    s = \"\" if value is None else str(value)\n    return s != \"\" and \"/\" not in s and \"\\\\\" not in s\n\nassert is_single_path_segment(args[\"file_id\"]), \"file_id must be one URL segment\"","typeGuard":"from typing import TypeGuard\n\ndef is_safe_path_segment(value: object) -> TypeGuard[str]:\n    if not isinstance(value, str) or not value:\n        return False\n    return \"/\" not in value and \"\\\\\" not in value and value not in (\".\", \"..\")","tryCatchPattern":"try:\n    safe = _sanitize_path_parameter_value(value, name)  # only if calling the generator directly\nexcept ValueError as e:\n    raise BadRequest(str(e)) from None\n# via the REST API: treat a tool result starting with \"Invalid path parameter:\" as a client-side 4xx","preventionTips":["Type tool inputs upstream: derive args from parsed/normalized values, never raw user strings.","Validate every path-typed argument with the single-segment guard before sending it to the proxy.","Map tool results starting with 'Invalid path parameter:' to your own 400, do not retry them."],"tags":["mcp","openapi","path-traversal","input-validation","url-encoding"],"backgroundTag":"path-traversal-blocked","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-24T22:17:12.610Z"}