From d105cc452a39ac1d0db4c702a41151989a8f5d7a Mon Sep 17 00:00:00 2001
From: Colin Harris
Date: Tue, 7 Jul 2026 17:05:22 +1000
Subject: [PATCH] fix: translate :timeout to :receive_timeout in discover/2
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
A2A.Client.discover/2 documents a :timeout option and take_req_opts/1
keeps it, but the option was passed straight to Req.new/1, which rejects
it (`unknown option :timeout`) — Req's key is :receive_timeout. Only the
message-send path translated it, via merge_req_opts/2.
Route discover/2's request options through merge_req_opts/2 so :timeout
behaves consistently across the client API. Adds a regression test.
Fixes #58
Co-Authored-By: Claude Opus 4.8
---
lib/a2a/client.ex | 9 ++++++---
test/a2a/client_test.exs | 11 +++++++++++
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/lib/a2a/client.ex b/lib/a2a/client.ex
index f8243a0..d00a1d0 100644
--- a/lib/a2a/client.ex
+++ b/lib/a2a/client.ex
@@ -138,9 +138,12 @@ if Code.ensure_loaded?(Req) do
path = Keyword.get(opts, :agent_card_path, "/.well-known/agent-card.json")
req_opts = take_req_opts(opts)
- base_opts = [base_url: base_url]
-
- req = Req.new(Keyword.merge(base_opts, req_opts))
+ # Route options through merge_req_opts/2 rather than passing them
+ # straight to Req.new/1: it translates :timeout into Req's
+ # :receive_timeout (Req has no :timeout option), matching how the
+ # message-send functions handle it and keeping the :timeout option
+ # documented on discover/2 working consistently.
+ req = merge_req_opts(Req.new(base_url: base_url), req_opts)
case Req.get(req, url: path) do
{:ok, %Req.Response{status: 200, body: body}} when is_map(body) ->
diff --git a/test/a2a/client_test.exs b/test/a2a/client_test.exs
index 9e3ca34..bfa3a14 100644
--- a/test/a2a/client_test.exs
+++ b/test/a2a/client_test.exs
@@ -92,6 +92,17 @@ defmodule A2A.ClientTest do
assert {:error, {:unexpected_status, 404}} =
Client.discover("https://agent.example.com", plug: plug)
end
+
+ test "accepts the :timeout option without raising" do
+ plug = fn conn ->
+ json_resp(conn, 200, @agent_card_json)
+ end
+
+ # :timeout must be translated to Req's :receive_timeout; passing it
+ # straight to Req.new/1 raises "unknown option :timeout".
+ assert {:ok, %AgentCard{name: "test-agent"}} =
+ Client.discover("https://agent.example.com", plug: plug, timeout: 60_000)
+ end
end
# -------------------------------------------------------------------