RestApiTool: integer / boolean / array header and cookie parameters crash the agent run (httpx: Header value must be str or bytes)
@llalitkumarrr ya está trabajando en esto.
Desde el 18/9/2026.
Evaluación
Este issue todavía no se ha evaluado.
Descripción
🔴 Required Information
Describe the Bug:
RestApiTool._prepare_request_params() passes header and cookie parameter values to httpx unchanged:
elif param_location == "header":
header_params[original_k] = v
elif param_location == "cookie":
cookie_params[original_k] = v
httpx serializes query values itself (5 becomes "5", False becomes "false"), but it only accepts str/bytes for headers and cookies. So when an OpenAPI spec declares an integer, number, boolean, or array header or cookie parameter, which is common in enterprise APIs (X-Page-Size, X-Api-Version, X-Dry-Run, ...), and the model fills it with a value of that type, the request can't be built. The TypeError isn't turned into a tool error, so the whole agent run fails.
This is separate from #7099, which handles None values for the same parameters.
Steps to Reproduce:
pip install google-adk(reproduced onmain@ f33d4923 / 2.9.0).- Run the script below: a real
RunnerplusLlmAgentplusOpenAPIToolsetwhose spec has an integer and a boolean header parameter. The model is mocked and HTTP goes to anhttpx.MockTransport.
Expected Behavior:
The request is sent with X-Page-Size: 50 and X-Include-Archived: false (OpenAPI's default simple style, and how httpx already encodes query values), and the tool result goes back to the model.
Observed Behavior:
File ".../httpx/_models.py", line 81, in _normalize_header_value
raise TypeError(f"Header value must be str or bytes, not {type(value)}")
TypeError: Header value must be str or bytes, not <class 'int'>
The exception propagates out of Runner.run_async(). An integer cookie parameter fails the same way (TypeError: expected string or bytes-like object, got 'int' from http.cookiejar). A boolean header would be sent as True/False even if it got through, rather than true/false.
Environment Details:
- ADK Library Version (pip show google-adk): 2.9.0 (
main@ f33d4923) - Desktop OS: Windows 11
- Python Version (python -V): 3.12.10
Model Information:
- Are you using LiteLLM: No
- Which model is being used: N/A (mocked model; the bug is in request building)
🟡 Optional Information
Minimal Reproduction Code:
import asyncio
import httpx
from google.adk.agents.llm_agent import LlmAgent
from google.adk.models.base_llm import BaseLlm
from google.adk.models.llm_response import LlmResponse
from google.adk.runners import InMemoryRunner
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset
from google.genai import types
spec = {
"openapi": "3.0.0",
"info": {"title": "Invoices", "version": "1"},
"servers": [{"url": "https://erp.example.com"}],
"paths": {"/invoices": {"get": {
"operationId": "listInvoices",
"parameters": [
{"name": "X-Page-Size", "in": "header", "required": True, "schema": {"type": "integer"}},
{"name": "X-Include-Archived", "in": "header", "schema": {"type": "boolean"}},
],
"responses": {"200": {"description": "ok"}},
}}},
}
sent = {}
def handler(request: httpx.Request):
sent.update(request.headers)
return httpx.Response(200, json={"invoices": []})
class FakeModel(BaseLlm):
model: str = "fake"
calls: int = 0
async def generate_content_async(self, llm_request, stream=False):
self.calls += 1
part = (types.Part.from_function_call(
name="list_invoices", args={"x_page_size": 50, "x_include_archived": False})
if self.calls == 1 else types.Part.from_text(text="done"))
yield LlmResponse(content=types.Content(role="model", parts=[part]))
agent = LlmAgent(
name="ap_agent",
model=FakeModel(),
tools=[OpenAPIToolset(
spec_dict=spec,
httpx_client_factory=lambda: httpx.AsyncClient(transport=httpx.MockTransport(handler)),
)],
)
async def main():
runner = InMemoryRunner(agent=agent)
session = await runner.session_service.create_session(app_name=runner.app_name, user_id="u")
async for _ in runner.run_async(
user_id="u", session_id=session.id,
new_message=types.Content(role="user", parts=[types.Part(text="list invoices")])):
pass
print("headers sent:", {k: v for k, v in sent.items() if k.startswith("x-")})
asyncio.run(main())
Suggested fix: serialize header and cookie values before handing them to httpx: numbers as str(v), booleans as true/false, arrays comma-joined. Leave other values alone, so None stays #7099's concern. I have a PR ready with tests.
How often has this issue occurred?:
- Always (100%): any non-string header or cookie parameter value.
- Lenguaje dominante
- Python
- Estrellas
- 21.6k
- Forks
- 4k
- Merge medio
- 13 h 49 min
- PR fusionados (30 d)
- 10
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de google/adk-python
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
google/adk-python#7217 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
google/adk-python#7206 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
google/adk-python#7205 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
google/adk-python#7196 · 1 comentario ·
-
eval request clarification
Dificultad 1/5 1-3 horas Aptitud para principiantes 86/100
google/adk-python#7146 · 2 comentarios · 1 asignado ·
Todos los issues de google/adk-python
Issues similares
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
zostera/django-bootstrap4#894 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
use-agent-os/agent-os#3276 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
zephyrproject-rtos/zephyr#119726 ·
-
area/auth bug comp/agent P3 platform/discord type/security
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
NousResearch/hermes-agent#117848 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
zilliztech/memsearch#759 ·