Skip to content

Unsanitized HostOs telemetry header crashes requests with LocalProtocolError: Illegal header value #1087

@watsonb

Description

@watsonb

Let me begin by stating that I'm primarily an Ansible content developer and user. I make heavy use of the azure.azcollection for Azure configuration management and general purpose automation. The azure.azcollection depends upon ms-graph-sdk, msgraph-core, and friends.

I came across this odd bug after patching my local system (Ubuntu 22.04.05 LTS) in the past couple/few days and it took me hours to track it down (sadly, I had to resort to Gemini/Claude assistance to track it down in a timely manner).

Bottom line, the root cause of the bug appears to be a local kernel update, where uname -v has whitespace at the end. I have a local patch in place inside telemetry.py, but I figure'd I'd share this report.

What follows is the result of my Claude/Gemini session identifying and patching the bug.

Describe the bug

TelemetryHandler._add_host_os_header sets the HostOs header to the raw value of platform.system() + " " + platform.version() without any sanitization:

# msgraph_core/middleware/telemetry.py
def _add_host_os_header(self, request) -> None:
    system = platform.system()
    version = platform.version()
    host_os = f'{system} {version}'
    request.headers.update({'HostOs': host_os})

platform.version() is whatever the OS reports for the kernel build string, and the SDK has no control over its contents. On some hosts this string contains leading/trailing whitespace (or other characters that are illegal in an HTTP header value). When it does, every request fails before it is sent, because h11 (via httpx/httpcore) rejects the header:

httpcore.LocalProtocolError: Illegal header value b'Linux #124~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 21:05:19 UTC '

(Note the trailing space — h11 rejects header values with leading/trailing whitespace.)

Expected behavior

Telemetry headers should be sanitized so a malformed platform.version() cannot crash requests — e.g. strip whitespace (and ideally collapse/replace any remaining illegal characters), or omit the header if it cannot be made valid. Telemetry must be best-effort and non-fatal.

How to reproduce

Any host whose platform.version() contains a trailing space (or other illegal header byte). Minimal reproduction of the failing condition:

import platform
from h11._headers import normalize_and_validate

# Simulating a host whose kernel build string ends with a trailing space:
version = "#124~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 21:05:19 UTC "
host_os = f"{platform.system()} {version}"
normalize_and_validate([(b"HostOs", host_os.encode())])
# -> h11._util.LocalProtocolError: Illegal header value b'Linux ... UTC '

In practice this surfaces through any Graph call. Real-world report: the Ansible azure.azcollection.azure_rm_adapplication module fails on the first GET /applications with the traceback above, on an Ubuntu 22.04 host (kernel 6.8.0-124-generic) whose uname -v ends with a trailing space and no year.

Suggested fix

host_os = f'{system} {version}'.strip()

A more defensive version would also collapse internal whitespace / drop non-token bytes, but .strip() resolves the reported case.

Environment

  • msgraph-core 1.1.3 (also present unchanged on main and through v1.4.0)
  • msgraph-sdk 1.6.0
  • httpx with h11 0.14.0
  • Python 3.10
  • OS: Ubuntu 22.04, kernel 6.8.0-124-generic

Full traceback (abridged)

File ".../msgraph_core/middleware/telemetry.py", line 48, in send
    response = await super().send(request, transport)
...
File ".../httpcore/_async/http11.py", line 151, in _send_request_headers
    with map_exceptions({h11.LocalProtocolError: LocalProtocolError}):
File ".../httpcore/_exceptions.py", line 14, in map_exceptions
    raise to_exc(exc) from exc
httpcore.LocalProtocolError: Illegal header value b'Linux #124~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 21:05:19 UTC '

The above exception was the direct cause of the following exception:
...
httpx.LocalProtocolError: Illegal header value b'Linux #124~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 21:05:19 UTC '

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions