langchain - ✅(Solved) Fix AzureChatOpenAI initialization is not backwards compatible - forcing User-Agent header override [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langchain-ai/langchain#35521Fetched 2026-04-08 00:25:50
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Author
Participants
Timeline (top)
closed ×1cross-referenced ×1labeled ×1referenced ×1

Fix Action

Fixed

PR fix notes

PR #35523: fix(openai): let user-provided User-Agent override the Azure default

Description (problem / solution / changelog)

Summary

  • Swapped dict unpacking order so the hardcoded User-Agent acts as a fallback instead of an override
  • Applied to all three Azure classes: AzureChatOpenAI, AzureOpenAI, and AzureOpenAIEmbeddings

Problem

The default_headers dict construction placed the hardcoded User-Agent after the user's headers:

"default_headers": {
    **(self.default_headers or {}),
    "User-Agent": "langchain-partner-python-azure-openai",  # always wins
},

This means any User-Agent set by the user via default_headers is silently overwritten, which caused 403 errors for users whose custom Azure endpoints require a specific User-Agent.

Fix

Swap the order so the hardcoded value comes first and user headers come second:

"default_headers": {
    "User-Agent": "langchain-partner-python-azure-openai",  # fallback
    **(self.default_headers or {}),  # user can override
},

In Python dict unpacking, later keys override earlier ones, so user-provided headers now take precedence.

Test plan

  • AzureChatOpenAI(default_headers={"User-Agent": "custom"}) — verify custom is used
  • AzureChatOpenAI() — verify default langchain-partner-python-azure-openai is still used

Note: This PR was generated with AI assistance (Claude Code).

Fixes #35521

🤖 Generated with Claude Code

Changed files

  • libs/partners/openai/langchain_openai/chat_models/azure.py (modified, +1/-1)
  • libs/partners/openai/langchain_openai/embeddings/azure.py (modified, +1/-1)
  • libs/partners/openai/langchain_openai/llms/azure.py (modified, +1/-1)

Code Example

def _inject_user_agent(_request: httpx.Request) -> None:
    _request.headers["User-Agent"] = HTTP_USER_AGENT


_azure_http_client_cached: httpx.Client | None = None


def _get_azure_http_client() -> httpx.Client | None:
    global _azure_http_client_cached

    if _azure_http_client_cached is None:
        _azure_http_client_cached = httpx.Client(event_hooks={"request": [_inject_user_agent]})
    return _azure_http_client_cached

---

# before line 662
              client_headers = self.default_headers or {}
              client_headers =  {k.lower(): v for k, v in client_headers.items()}
              if 'user-agent' not in client_headers:
                   client_headers['user-agent'] = "langchain-partner-python-azure-openai"
             # line 667
             'default_headers': client_headers
RAW_BUFFERClick to expand / collapse

https://github.com/langchain-ai/langchain/blob/11270174aa857c517ea755b4f50a7b23e018e995/libs/partners/openai/langchain_openai/chat_models/azure.py#L676

Upgrading langchain_openai from v0.2.2 to v1.1.9 caused our previously working calls to a custom azure_endpoint to start returning 403. Debugging the package showed that it forcibly overrides the User-Agent header to "langchain-partner-python-azure-openai", which some endpoints (including our custom one) do not accept.

In order to move past this issue - one will have to pass an http_client to AzureChatOpenAI, so it would be used to override the override:

def _inject_user_agent(_request: httpx.Request) -> None:
    _request.headers["User-Agent"] = HTTP_USER_AGENT


_azure_http_client_cached: httpx.Client | None = None


def _get_azure_http_client() -> httpx.Client | None:
    global _azure_http_client_cached

    if _azure_http_client_cached is None:
        _azure_http_client_cached = httpx.Client(event_hooks={"request": [_inject_user_agent]})
    return _azure_http_client_cached

This change is not backwards compatible in this sense and it seems like the author desire was to set this header as default, and not override the existing default. Otherwise there should've been at least a note added in the constructor's attribute of 'default_headers' regarding the fact that passing user-agent header won't have effect. Also, it was done specifically for the client_params, where if one would inspect the azure instance's default_headers, he will see the user-agent header he set himself, and not the actual one that is used in the http client.

Possible fix would be to check for existence of this header and set it according to the author's request if not set:

              # before line 662
              client_headers = self.default_headers or {}
              client_headers =  {k.lower(): v for k, v in client_headers.items()}
              if 'user-agent' not in client_headers:
                   client_headers['user-agent'] = "langchain-partner-python-azure-openai"
             # line 667
             'default_headers': client_headers

extent analysis

Fix Plan

Step 1: Create a custom HTTP client

Create a new file custom_http_client.py with the following code:

import httpx

HTTP_USER_AGENT = "langchain-partner-python-azure-openai"

def _inject_user_agent(_request: httpx.Request) -> None:
    _request.headers["User-Agent"] = HTTP_USER_AGENT

_azure_http_client_cached: httpx.Client | None = None

def _get_azure_http_client() -> httpx.Client | None:
    global _azure_http_client_cached

    if _azure_http_client_cached is None:
        _azure_http_client_cached = httpx.Client(event_hooks={"request": [_inject_user_agent]})
    return _azure_http_client_cached

Step 2: Pass the custom HTTP client to AzureChatOpenAI

Update your code to pass the custom HTTP client to AzureChatOpenAI:

from custom_http_client import _get_azure_http_client

azure_http_client = _get_azure_http_client()
azure_chat_openai = AzureChatOpenAI(http_client=azure_http_client)

Step 3: Verify the fix

Verify that the custom HTTP client is being used by checking the User-Agent header in the requests. You can use a tool like curl or a debugging proxy to inspect the requests.

Verification

  • Check the User-Agent header in the requests to ensure it matches the custom user agent.
  • Verify that the requests are being sent successfully without any 403 errors.

Extra Tips

  • Make sure to update your code to use the custom HTTP client for all AzureChatOpenAI instances.
  • Consider adding a check to ensure that the custom HTTP client is being used correctly in case of any future updates to the langchain_openai package.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

langchain - ✅(Solved) Fix AzureChatOpenAI initialization is not backwards compatible - forcing User-Agent header override [1 pull requests, 1 participants]