googleapis / googleapis/python-genai
Interactions client sends duplicate User-Agent header (breaks aiohttp 3.13.4+ servers)
- Dominant language
- Python
- Stars
- 4k
- Forks
- 1k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 40
Description
## Description
The Interactions API client sends two `User-Agent` HTTP headers on every request, which causes strict HTTP servers (aiohttp 3.13.4+) to reject the request with `400 Bad Request: Duplicate 'User-Agent' header found`.
## Root cause
`append_library_version_headers()` in `_api_client.py` adds a **lowercase** `user-agent` key to the headers dict:
```python
headers['user-agent'] = f'{library_label} {language_label}'
```
The Stainless-generated base class (`AsyncAPIClient.default_headers`) adds a **title-case** `User-Agent` key via its `user_agent` property:
```python
@property
def default_headers(self) -> dict[str, str | Omit]:
return {
"User-Agent": self.user_agent,
...
**self._custom_headers, # ← contains lowercase 'user-agent' from adapter
}
```
Since Python dicts are case-sensitive, the merged dict has both `User-Agent` and `user-agent` as separate keys. When passed to `httpx.Headers()`, they become two distinct header entries on the wire:
```
user-agent: AsyncGeminiNextGenAPIClient/Python 1.71.0
user-agent: google-genai-sdk/1.71.0 gl-python/3.14.3
```
## Reproduction
```python
from google import genai
from google.genai import types
import httpx
client = genai.Client(
api_key="test",
http_options=types.HttpOptions(api_version="v1beta", base_url="http://localhost:8080"),
)
ic = client.aio.interactions._client
h = httpx.Headers(ic.default_headers)
print(h.get_list("user-agent"))
# ['AsyncGeminiNextGenAPIClient/Python 1.71.0', 'google-genai-sdk/1.71.0 gl-python/3.14.3']
```
## Impact
- Any HTTP server using aiohttp 3.13.4+ as request parser rejects the request per RFC 9110 §5.5
- Affects all users of the Interactions API talking to proxies, test servers, or middleware that enforce singleton headers
- Reported independently in home-assistant/core#166866
## Affected versions
Tested on 1.71.0 and 1.72.0. Likely present since Interactions API was introduced.
## Suggested fix
Unify the User-Agent to a single key in `append_library_version_headers()`:
```python
# Use title-case to match the Stainless base class key
headers['User-Agent'] = f'{library_label} {language_label}'
# Or remove the base class User-Agent and let the adapter be the sole source
```
## Environment
- google-genai: 1.71.0 / 1.72.0
- Python: 3.14.3
- aiohttp (server): 3.13.4+ (rejects), 3.13.3 (accepts)
Contributor guide
Assessment
This issue has not been assessed yet.