microsoft/semantic-kernel · error · FunctionExecutionException

The request URI '{url}' is not allowed. It does not match an

Error message

The request URI '{url}' is not allowed. It does not match any of the allowed base URLs.

What it means

Thrown by validate_server_url when you have configured a non-empty allowed_base_urls allow-list but the fully resolved request URL does not match any entry. Matching requires scheme, hostname, port, and path-prefix to all align (host/path compared case-insensitively). This is the explicit allow-list enforcement path of the OpenAPI plugin SSRF guard.

Source

Thrown at python/semantic_kernel/connectors/openapi_plugin/server_url_validator.py:50

async def validate_server_url(
    url: str,
    options: ServerUrlValidationOptions | None = None,
    dns_resolver: DnsResolver | None = None,
) -> None:
    """Validate a fully resolved OpenAPI operation URL against the supplied policy."""
    options = options or ServerUrlValidationOptions()
    try:
        parsed_url = _parse_absolute_url(url)
    except ValueError as exc:
        raise FunctionExecutionException(
            f"The request URI '{url}' is not allowed because it is not a valid absolute URI."
        ) from exc

    if _matches_allowed_base_url(parsed_url, options.allowed_base_urls):
        return

    if options.allowed_base_urls:
        raise FunctionExecutionException(
            f"The request URI '{url}' is not allowed. It does not match any of the allowed base URLs."
        )

    if parsed_url.scheme.lower() != DEFAULT_ALLOWED_SCHEME:
        raise FunctionExecutionException(
            f"The request URI scheme '{parsed_url.scheme}' is not allowed. "
            f"Only '{DEFAULT_ALLOWED_SCHEME}' is permitted by default. "
            "To allow this URL, add it to server_url_validation_allowed_base_urls."
        )

    if options.allow_private_network_access:
        return

    await _ensure_public_host(parsed_url, dns_resolver)


def try_categorize_non_public_address(
    address: str | ipaddress.IPv4Address | ipaddress.IPv6Address,

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Add the operation's exact base URL (scheme + host + port + path prefix) to ServerUrlValidationOptions.allowed_base_urls or the server_url_validation_allowed_base_urls setting
  2. Verify scheme, hostname, port, and path-prefix all match an allowed entry; host and path matching is case-insensitive
  3. Confirm the OpenAPI spec's servers entry matches what you allow-listed
  4. If the destination is legitimately trusted and private access is intended, set allow_private_network_access=True instead of widening the allow-list carelessly

Example fix

# before
options = ServerUrlValidationOptions(allowed_base_urls=['https://api.example.com'])
await validate_server_url('https://api.example.com/v2/search', options)  # path /v2 not allowed

# after
options = ServerUrlValidationOptions(allowed_base_urls=['https://api.example.com'])
await validate_server_url('https://api.example.com/v2/search', options)  # base path '/' matches
# or, allow only the v2 API:
options = ServerUrlValidationOptions(allowed_base_urls=['https://api.example.com/v2'])
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def url_matches_allowed(url: str, allowed_base_urls: list[str]) -> bool:
    def parts(u: str):
        p = urlparse(u)
        port = p.port or (443 if p.scheme.lower() == 'https' else 80 if p.scheme.lower() == 'http' else None)
        return p.scheme.lower(), (p.hostname or '').lower(), port, (p.path or '/')
    u = parts(url)
    for base in allowed_base_urls:
        b = parts(base)
        if u[0] == b[0] and u[1] == b[1] and u[2] == b[2] and (u[3] == b[3] or u[3].startswith(b[3].rstrip('/') + '/')):
            return True
    return False

# call before validate_server_url
if not url_matches_allowed(op_url, options.allowed_base_urls):
    raise ValueError(f'{op_url} not in allow-list')

Try / catch

from semantic_kernel.exceptions.function_exceptions import FunctionExecutionException

try:
    await validate_server_url(url, options)
except FunctionExecutionException as e:
    if 'does not match any of the allowed base URLs' in str(e):
        # add url base to options.allowed_base_urls or reject the operation
        ...

Prevention

When it happens

Trigger: An OpenAPI plugin operation resolves to a URL outside every configured base. Example: allowed_base_urls=['https://api.example.com/v1'] but an operation's server URL is https://api.other.com/v1/foo, or the path lies outside the /v1 prefix, or the scheme/port differ.

Common situations: OpenAPI spec lists a server URL that differs from the allow-listed base; a new endpoint lives on a different domain/subdomain; the allowed base URL was mistyped or is missing its path prefix; port mismatch (base on 443, request on 8443).

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/ed0f16d16cb83a3e. Report an issue: GitHub.