Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

[Bug]: REST tenant routes are mounted before `path_prefix`, so tenant requests from the SDK client get 404

Abierto
#1,376 0 comentarios 0 reacciones 2 asignados Ver en GitHub

Los mantenedores suelen responder en 2 días

@rohityan ya está trabajando en esto.

Desde el 25/10/2026.

  • #1377 de @Revinand — abierto

Evaluación

Este issue todavía no se ha evaluado.

Descripción

What happened?

With a non-empty path_prefix, create_rest_routes serves the tenant routes in front of the prefix, at /{tenant}{path_prefix}/.... The SDK's own REST client sends the tenant after the prefix, at {path_prefix}/{tenant}/..., so a client whose AgentInterface sets tenant gets a 404 on every call.

In src/a2a/server/routes/rest_routes.py, the tenant routes come from Mount(path='/{tenant}', routes=base_route_objects), and base_route_objects already include path_prefix. On the client side, RestTransport._get_path returns /{tenant}{base_path}, which is appended to the interface URL. The HTTP bindings in a2a.proto also put the tenant right after the interface URL (post: "/{tenant}/message:send"). The hello_world_agent sample serves REST with path_prefix='/a2a/rest' and advertises an interface URL ending in /a2a/rest, so the prefix is meant to be part of the interface URL.

This changed in #892. Before it, the REST adapter built /{tenant}{path} and the FastAPI app added rpc_url in front, so the prefix came first. Without a prefix both layouts are the same, so only servers that set path_prefix are affected.

To reproduce, pip install "a2a-sdk[http-server]==1.2.3" and run:

import asyncio
from unittest.mock import AsyncMock

import httpx
from starlette.applications import Starlette

from a2a.client import ClientConfig, ClientFactory
from a2a.server.request_handlers import RequestHandler
from a2a.server.routes import create_rest_routes
from a2a.types import AgentCard, AgentInterface, ListTasksRequest, ListTasksResponse

handler = AsyncMock(spec=RequestHandler)
handler.on_list_tasks.return_value = ListTasksResponse()
app = Starlette(routes=create_rest_routes(handler, path_prefix="/a2a/rest"))

card = AgentCard(supported_interfaces=[AgentInterface(
    url="http://test/a2a/rest", protocol_binding="HTTP+JSON",
    protocol_version="1.0", tenant="acme")])


async def main():
    async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url="http://test") as http:
        for path in ("/a2a/rest/acme/tasks", "/acme/a2a/rest/tasks"):
            r = await http.get(path, headers={"A2A-Version": "1.0"})
            print(path, r.status_code)
        client = ClientFactory(ClientConfig(httpx_client=http, supported_protocol_bindings=["HTTP+JSON"])).create(card)
        try:
            await client.list_tasks(ListTasksRequest())
            print("list_tasks: ok")
        except Exception as e:
            print(f"list_tasks: {type(e).__name__}: {e}")


asyncio.run(main())

I expected /a2a/rest/acme/tasks to return 200 and list_tasks to succeed.

I have a fix with a test and will open a PR.

Relevant log output
/a2a/rest/acme/tasks 404
/acme/a2a/rest/tasks 200
list_tasks: MethodNotFoundError: Resource not found: http://test/a2a/rest/acme/tasks
Code of Conduct
  • I agree to follow this project's Code of Conduct
Lenguaje dominante
Python
Estrellas
2.2k
Forks
518
Merge medio
3 d 7 h
PR fusionados (30 d)
38

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de a2aproject/a2a-python

Todos los issues de a2aproject/a2a-python

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.