Skip to content

fix(a2a): forward use_cache=False from fetch_agent_card to afetch_agent_card - #6732

Open
LHMQ878 wants to merge 1 commit into
crewAIInc:mainfrom
LHMQ878:fix/a2a-fetch-agent-card-use-cache
Open

fix(a2a): forward use_cache=False from fetch_agent_card to afetch_agent_card#6732
LHMQ878 wants to merge 1 commit into
crewAIInc:mainfrom
LHMQ878:fix/a2a-fetch-agent-card-use-cache

Conversation

@LHMQ878

@LHMQ878 LHMQ878 commented Jul 30, 2026

Copy link
Copy Markdown

Closes #6731

Problem

fetch_agent_card(endpoint, use_cache=False) still returns a cached AgentCard. The
uncached branch of the sync entry point delegates to the async entry point without
forwarding use_cache:

    coro = afetch_agent_card(endpoint=endpoint, auth=auth, timeout=timeout)
    #                                                     ^ use_cache dropped

afetch_agent_card declares use_cache: bool = True, so the request re-enters the cached
branch and is answered by _afetch_agent_card_cached, which carries
@cached(ttl=300, serializer=PickleSerializer()). An explicit use_cache=False is silently
reinterpreted as use_cache=True, and the caller can receive a card up to 5 minutes stale
with no exception and no warning.

The async entry point already honours use_cache=False, so before this change the two
entry points disagreed about the meaning of the same argument.

Fix

-    coro = afetch_agent_card(endpoint=endpoint, auth=auth, timeout=timeout)
+    # afetch_agent_card defaults use_cache=True, so it has to be forwarded: without
+    # it this uncached request is served from the async cache.
+    coro = afetch_agent_card(
+        endpoint=endpoint, auth=auth, timeout=timeout, use_cache=False
+    )

One argument. The cached branch, the cache_ttl/ttl_hash machinery, the running-loop
handling and the auth-hash key derivation are all untouched.

Evidence

Patching _afetch_agent_card_impl with a counter that returns a distinguishable card per
real fetch, and calling each entry point twice:

path before after correct
fetch_agent_card(..., use_cache=False) 1 fetchprobe-agent-1, probe-agent-1 2 fetches — probe-agent-1, probe-agent-2 2
fetch_agent_card(..., use_cache=True) 1 fetch 1 fetch 1
await afetch_agent_card(..., use_cache=False) 2 fetches 2 fetches 2

Rows 2 and 3 are the important ones: the fix does not weaken the cache, and it brings the
sync path in line with the async path that was already right.

Tests

Three tests in a new TestFetchAgentCardCaching class in
lib/crewai/tests/a2a/utils/test_agent_card.py. A counting_fetch fixture monkeypatches
_afetch_agent_card_impl with a counter returning card-1, card-2, … so a cache hit is
directly observable in the returned object rather than inferred.

test asserts role
test_sync_use_cache_false_refetches 2 real fetches, names card-1/card-2 the regression
test_sync_use_cache_true_still_caches 1 real fetch, both names card-1 control: cache still works
test_async_use_cache_false_refetches 2 real fetches, names card-1/card-2 control: async unchanged

Control experiment

Reverting agent_card.py and keeping the tests:

1 failed, 2 passed
FAILED lib/crewai/tests/a2a/utils/test_agent_card.py::TestFetchAgentCardCaching::test_sync_use_cache_false_refetches
  assert len(counting_fetch) == 2

Exactly the one regression test fails; both controls pass, so they are not passing for the
wrong reason and the new test is genuinely pinned to this change.

Suite runs

  • lib/crewai/tests/a2a/utils/test_agent_card.py — 24 passed (21 pre-existing + 3 new)
  • lib/crewai/tests/a2a/ — 104 passed, 0 failed
  • ruff format --check / ruff check on agent_card.py — clean (the tests/ tree is in
    extend-exclude, so ruff skips it by design)
  • mypy lib/crewai/src/crewai/a2a/utils/agent_card.pySuccess: no issues found

One note on how I ran these locally: addopts includes --block-network, and on Windows
asyncio.run() builds a ProactorEventLoop whose _make_self_pipe() calls
socket.socketpair(), which falls back to a real connect() to localhost and trips
pytest_recording's guard. Since fetch_agent_card's uncached path goes through
asyncio.run, I ran with --allowed-hosts=127.0.0.1,::1 to let the loop's self-pipe
through. This is a Windows-only artifact of socketpair() — on Linux it is a real
socketpair(2) syscall that never touches connect, so CI needs no such flag.

Related, deliberately not included

fetch_agent_card takes cache_ttl: int = 300 and honours it (ttl_hash = int(time.time() // cache_ttl) as a cache-key component — verified with cache_ttl=1 and
two calls 1.1s apart producing 2 real fetches). afetch_agent_card has no cache_ttl
parameter at all; its cache is a hardcoded @cached(ttl=300). Giving the async side a
tunable TTL means reworking the decorator, which is a feature change rather than a bug fix,
so I left it out and described it in #6731 instead.

Duplicate check

Searched the repo for fetch_agent_card, use_cache agent card, and agent_card.py in
titles. Prior hits: #4672 and #4675 (both closed, running-event-loop handling in the sync
wrappers — different defect, same function), #5949 (closed).

One overlap I want to flag rather than have a reviewer discover: #5852
(feat(a2a): add optional signature verification to AgentCard fetch, open) adds
use_cache=False to this same call as an incidental part of a much larger signature-
verification feature. That PR was opened 2026-05-18, has only bot reviews, was marked stale
by the workflow on 2026-07-04, is currently behind its base, and contains no test for the
caching behaviour. If maintainers prefer to land #5852 instead, this PR's tests still apply
to it unchanged and I am happy to close this and offer them there — but the caching bug
seems worth fixing on its own, with coverage, independent of whether a feature PR from
two and a half months ago is revived.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d93c10c-68e2-4454-b618-db459576eea3

📥 Commits

Reviewing files that changed from the base of the PR and between ceed4a3 and 5610b7e.

📒 Files selected for processing (2)
  • lib/crewai/src/crewai/a2a/utils/agent_card.py
  • lib/crewai/tests/a2a/utils/test_agent_card.py

📝 Walkthrough

Walkthrough

The sync agent card fetcher now forwards use_cache=False to the async implementation. Tests verify uncached refetching for sync and async calls, while confirming cached sync calls still reuse the result.

Changes

Agent card caching

Layer / File(s) Summary
Forward uncached fetch flag
lib/crewai/src/crewai/a2a/utils/agent_card.py
The uncached sync path calls afetch_agent_card with use_cache=False.
Cache behavior regression tests
lib/crewai/tests/a2a/utils/test_agent_card.py
Added a counting fixture and tests covering sync uncached refetches, sync cached reuse, and async uncached refetches.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main fix: forwarding use_cache=False from the sync entry point to the async one.
Description check ✅ Passed The description is directly related to the bug fix and regression tests, and it matches the reported change.
Linked Issues check ✅ Passed The PR forwards use_cache=False correctly and adds tests showing sync uncached calls refetch while cached and async behavior stay intact.
Out of Scope Changes check ✅ Passed The added tests and one-line code change stay within the issue scope, with no unrelated behavior changes evident.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…nt_card

fetch_agent_card's uncached branch delegated to afetch_agent_card without
forwarding use_cache. afetch_agent_card declares use_cache: bool = True, so the
request re-entered the cached branch and was answered by
_afetch_agent_card_cached (@cached(ttl=300)) -- an explicit use_cache=False was
silently reinterpreted as use_cache=True and could return a card up to 5 minutes
stale.

The async entry point already honours use_cache=False, so the two entry points
disagreed on the meaning of the same argument.

Adds three tests: one for the defect, plus controls confirming the sync cached
path still caches and the async path was already correct.

Closes crewAIInc#6731
@LHMQ878
LHMQ878 force-pushed the fix/a2a-fetch-agent-card-use-cache branch from 5610b7e to 4afb1bc Compare July 30, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] fetch_agent_card(use_cache=False) still returns a cached AgentCard

1 participant