Skip to content

[Bug]: gRPC reference implementation resolves the Agent Card eagerly, unlike the JSON-RPC and REST ones #1033

Description

@omatheusmesmo

What happened?

Context: I am building an A2A server extension for Quarkus LangChain4j. It started from a stalled PR by another contributor, who is not involved in this work; that PR predates the 1.x SDK, and most of the current code is my own, bringing it to 1.2.0.Final. This surfaced when I added gRPC coverage to the integration tests. The goal is to land the extension in quarkus-langchain4j proper.

The three Quarkus reference implementations disagree about when the Agent Card is resolved, and the gRPC one resolves it earliest, early enough that an application whose card depends on the address the HTTP server bound to cannot start at all.

Module How it obtains the card
a2a-java-sdk-reference-jsonrpc A2AServerRoutes holds no AgentCard field; the card is resolved per request
a2a-java-sdk-reference-rest same
a2a-java-sdk-reference-grpc QuarkusGrpcHandler declares private final AgentCard agentCard and private final AgentCard extendedAgentCard, both constructor parameters

What makes this awkward is that the constructor already receives Instance<AgentCard> for the extended card, the very mechanism that exists to defer resolution, and still resolves it inside the constructor and stores it in a final field.

Why it breaks startup. Quarkus's GrpcServerRecorder.collectServiceDefinitions runs at RUNTIME_INIT and instantiates the handler. Quarkus fires HttpServerStart with fireAsync from WebDeploymentVerticle, that is, later. So any @PublicAgentCard producer that waits for the bound address is invoked before the address exists, and the application never starts.

Deriving the card's URL from the address actually bound is not an exotic thing to want: it is the only way to be correct with quarkus.http.port=0, and it is what makes the card right in tests (port 8081) and in production (port 8080) without configuring the URL twice.

Suggested fix

Hold Instance<AgentCard> in both fields and resolve inside getAgentCard() / getExtendedAgentCard(), which would also bring the gRPC module in line with the JSON-RPC and REST ones.

My workaround

I fall back to quarkus.http.host / quarkus.http.port when the startup event has not arrived yet, and still fail as before when the port is 0 or plain HTTP is disabled, cases where a guessed URL would be wrong and a client may cache it. I intend to keep that fallback even after an upstream fix, because fireAsync makes this a race for the other transports too.

Verified against 1.2.0.Final

QuarkusGrpcHandler still declares two private final AgentCard fields; the JSON-RPC and REST A2AServerRoutes declare none.

Happy to send a PR for the Instance<AgentCard> change if you agree with the direction.

Relevant log output

java.lang.IllegalStateException: The address the HTTP server is bound to is not known yet
    at ConfigCardBuilderCustomizer.determineUrl
    at AgentCardProducer.agentCard
    at QuarkusGrpcHandler_Bean.create
    at io.quarkus.grpc.runtime.GrpcServerRecorder.collectServiceDefinitions(GrpcServerRecorder.java:487)

Environment

  • a2a-java 1.2.0.Final (originally found on 1.1.0.Final)
  • Quarkus 3.33.2

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions