microsoft/semantic-kernel · error · FunctionExecutionException

The request URI '{parsed_url.geturl()}' is not allowed: DNS

Error message

The request URI '{parsed_url.geturl()}' is not allowed: DNS resolution for host '{host}' returned no addresses. The request is blocked as a precaution.

What it means

Thrown when DNS resolution for the host succeeded but returned zero addresses. The validator treats a host that resolves to nothing as suspicious and blocks the request as a precaution against SSRF.

Source

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

    base_path_with_slash = base_path if base_path.endswith("/") else f"{base_path}/"
    return url_path.lower().startswith(base_path_with_slash.lower())


async def _ensure_public_host(parsed_url: ParseResult, dns_resolver: DnsResolver | None) -> None:
    host = parsed_url.hostname
    if host is None:
        raise FunctionExecutionException(f"The request URI '{parsed_url.geturl()}' does not contain a valid host.")

    try:
        ip_address = ipaddress.ip_address(host)
    except ValueError:
        addresses = await _resolve_host(host, dns_resolver)
    else:
        _ensure_public_address(parsed_url.geturl(), ip_address)
        return

    if not addresses:
        raise FunctionExecutionException(
            f"The request URI '{parsed_url.geturl()}' is not allowed: DNS resolution for host "
            f"'{host}' returned no addresses. The request is blocked as a precaution."
        )

    for address in addresses:
        _ensure_public_address(parsed_url.geturl(), address)


async def _resolve_host(
    host: str,
    dns_resolver: DnsResolver | None,
) -> list[ipaddress.IPv4Address | ipaddress.IPv6Address]:
    try:
        if dns_resolver:
            resolved_addresses = await dns_resolver(host)
            return [ipaddress.ip_address(address) for address in resolved_addresses]

        loop = asyncio.get_running_loop()

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Verify the hostname has A/AAAA records (dig a <host> / dig aaaa <host>)
  2. Correct the hostname typo
  3. If using a custom dns_resolver, ensure it returns IP address strings for valid hosts
  4. Add the trusted base URL to allowed_base_urls to bypass DNS checks once safety is confirmed
Defensive patterns

Strategy: try-catch

Validate before calling

import socket

def host_has_records(host: str) -> bool:
    try:
        return len(socket.getaddrinfo(host, None, type=socket.SOCK_STREAM)) > 0
    except OSError:
        return False

if not host_has_records(urlparse(url).hostname):
    raise ValueError(f'{url} resolves to no addresses')

Try / catch

try:
    await validate_server_url(url, options)
except FunctionExecutionException as e:
    if 'returned no addresses' in str(e):
        # verify DNS records exist or add the host to allowed_base_urls
        ...

Prevention

When it happens

Trigger: A syntactically valid hostname that has no A/AAAA records (stale DNS, typo'd subdomain, a domain with only MX/CNAME records). _resolve_host returns an empty list.

Common situations: Domain typo; decommissioned host; split-horizon DNS returning nothing in the current environment; a custom dns_resolver returning an empty sequence.

Understand the failure class

Related errors


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