PrefectHQ/fastmcp · warning · ValueError

Excluded headers must be lowercase

Error message

Excluded headers must be lowercase

What it means

get_http_headers(include, ...) builds a default set of lowercase excluded header names; when a custom include set is provided, FastMCP sanity-checks that every exclusion entry is already lowercase before doing case-sensitive set math. This ValueError is thrown for non-lowercase exclude entries, preventing subtle filtering bugs from HTTP header case-insensitivity.

Source

Thrown at fastmcp_slim/fastmcp/server/dependencies.py:585

            "upgrade",
            "te",
            "keep-alive",
            "expect",
            "accept",
            "authorization",
            "cookie",
            # Proxy-related headers
            "proxy-authenticate",
            "proxy-authorization",
            "proxy-connection",
            # MCP-related headers
            "mcp-session-id",
        }
        if include:
            exclude_headers -= {h.lower() for h in include}
        # Sanity check: all entries must already be lowercase
        if not all(h.lower() == h for h in exclude_headers):
            raise ValueError("Excluded headers must be lowercase")
    headers: dict[str, str] = {}

    try:
        source: Any = get_http_request().headers.items()
    except RuntimeError:
        # No live request: inside a background-task worker, fall back to the
        # headers the task carried from its originating request (set by the
        # tasks extension from the snapshot). Empty elsewhere.
        task_headers = _background_task_headers.get()
        if task_headers is None:
            return {}
        source = task_headers.items()

    for name, value in source:
        lower_name = name.lower()
        if lower_name not in exclude_headers:
            headers[lower_name] = str(value)
    return headers

View on GitHub (pinned to 1f02114297)

Solutions

  1. Pass header names in include/exclude entirely lowercase (e.g. 'authorization', not 'Authorization').
  2. Normalize with name.lower() before calling get_http_headers.
  3. HTTP headers are matched case-insensitively downstream, so lowercasing loses nothing.

Example fix

// before
get_http_headers(include={"Authorization"})
// after
get_http_headers(include={"authorization"})
Defensive patterns

Strategy: validation

Validate before calling

names = {"Authorization"}
assert all(h == h.lower() for h in names), "header names must be lowercase"
get_http_headers(include=names)

Type guard

def all_lowercase(names: set[str]) -> bool:
    return all(n == n.lower() for n in names)

Try / catch

try:
    headers = get_http_headers(include=my_headers)
except ValueError as e:
    if "Excluded headers must be lowercase" in str(e):
        headers = get_http_headers(include={h.lower() for h in my_headers})
    else:
        raise

Prevention

When it happens

Trigger: Calling get_http_headers() while the computed exclusion set contains an entry with uppercase characters — i.e. the include parameter or an internal default contributed a mixed-case header name (e.g. 'Authorization').

Common situations: Passing include={'Authorization'} or similarly cased names to get_http_headers; copy-pasting HTTP header names in their canonical Capitalized form.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/058905247be5cbf7. Report an issue: GitHub.