Repository navigation
adk api_server --a2a fails to register A2A routes in google-adk==2.2.0 #5994
Description
🔴 Required Information
Describe the Bug:
After upgrading google-adk from 2.1.0 to 2.2.0, running adk api_server --a2a --with_ui starts the FastAPI server successfully but fails to register A2A routes for an agent configured with agent.json.
The server logs the following during startup:
Failed to setup A2A agent orchestrator: cannot access local variable 'json' where it is not associated with a value
As a result, the expected A2A agent card endpoint is not registered and returns 404:
GET /a2a/orchestrator/.well-known/agent-card.json HTTP/1.1" 404 Not Found
This appears to be caused by a function-local import json inside get_fast_api_app() in google/adk/cli/fast_api.py. The function uses json.load(...) earlier during A2A route setup, but because json is later imported inside the same function, Python treats json as a local variable throughout the whole function scope. This causes an UnboundLocalError before the local import has executed.
Steps to Reproduce:
-
Install
google-adk==2.2.0.uv add google-adk==2.2.0
-
Create an ADK agents directory containing an agent subdirectory with an
agent.jsonfile.Example structure:
orchestrator_demo/ orchestrator/ agent.json -
Start the ADK API server with A2A enabled.
uv run adk api_server --a2a --with_ui orchestrator_demo \ --host 127.0.0.1 \ --port 8000 \ --session_service_uri sqlite:///sessions.sqlite \ --artifact_service_uri file:///artifacts
-
Observe the server startup logs.
Setting up A2A agent: orchestrator Failed to setup A2A agent orchestrator: cannot access local variable 'json' where it is not associated with a value Application startup complete. -
Attempt to access the expected A2A agent card endpoint.
curl http://127.0.0.1:8000/a2a/orchestrator/.well-known/agent-card.json
-
Observe that the endpoint returns
404 Not Found.
Expected Behavior:
ADK should load:
orchestrator_demo/orchestrator/agent.json
Then create the A2A application and register A2A routes, including:
/a2a/orchestrator
/a2a/orchestrator/.well-known/agent-card.json
The agent card endpoint should return the configured A2A agent card instead of 404.
Observed Behavior:
The FastAPI server starts and reports startup completion, but A2A route setup fails internally.
Relevant startup log:
Setting up A2A agent: orchestrator
Failed to setup A2A agent orchestrator: cannot access local variable 'json' where it is not associated with a value
Application startup complete.
The expected A2A route is not registered:
GET /a2a/orchestrator/.well-known/agent-card.json HTTP/1.1" 404 Not Found
The server appears healthy, but A2A discovery and message flow are unavailable because the A2A endpoints are missing.
Environment Details:
- ADK Library Version (
pip show google-adk):2.2.0 - Desktop OS: N/A
- Python Version (
python -V):Python 3.11
Model Information:
- Are you using LiteLLM: No
- Which model is being used: N/A
🟡 Optional Information
Regression:
Yes. This worked in google-adk==2.1.0 and fails after upgrading to google-adk==2.2.0.
Logs:
Setting up A2A agent: orchestrator
Failed to setup A2A agent orchestrator: cannot access local variable 'json' where it is not associated with a value
Application startup complete.
GET /a2a/orchestrator/.well-known/agent-card.json HTTP/1.1" 404 Not Found
Screenshots / Video:
N/A
Additional Context:
The suspected root cause is in google/adk/cli/fast_api.py, inside get_fast_api_app().
The function uses json.load(...) while setting up A2A routes. Later in the same function, there is a function-local import:
import jsonBecause Python treats any name assigned or imported inside a function as local to the entire function scope, the earlier json.load(...) resolves json as a local variable before it has been assigned. This causes:
UnboundLocalError: cannot access local variable 'json' where it is not associated with a value
This can occur even if the later conditional branch containing import json is not executed, because Python determines local variables at function compile time.
The module already imports json at module scope, so the later function-local import json appears unnecessary.
Suggested fix:
- import inspect
- import json
+ import inspectAlternatively, move all required imports to module scope and rely on the existing module-level json import.
A regression test could call get_fast_api_app(..., a2a=True, web=True) against a temporary agents directory containing:
orchestrator/
agent.json
Then assert that the generated FastAPI app includes routes for:
/a2a/orchestrator
/a2a/orchestrator/.well-known/agent-card.json
Minimal Reproduction Code:
# Minimal reproduction is primarily CLI-based.
# Directory structure:
#
# orchestrator_demo/
# orchestrator/
# agent.json
#
# Then run:
#
# uv run adk api_server --a2a --with_ui orchestrator_demo \
# --host 127.0.0.1 \
# --port 8000 \
# --session_service_uri sqlite:///sessions.sqlite \
# --artifact_service_uri file:///artifacts
#
# Then request:
#
# curl http://127.0.0.1:8000/a2a/orchestrator/.well-known/agent-card.json
#
# Expected:
# Agent card JSON
#
# Actual:
# 404 Not FoundHow often has this issue occurred?:
- Always (100%)
Activity
adk-bot commentedon Jun 6, 2026
GitHub Issue #5994 Analysis: adk api_server --a2a fails to register A2A routes in google-adk==2.2.0
Executive Summary
adk api_server --a2a) fails to initialize A2A routes and raisesUnboundLocalError: cannot access local variable 'json' where it is not associated with a valuedue to a function-localimport jsoninside get_fast_api_app.google-adk==2.2.0when started via the CLI. It has zero risk and is extremely simple to resolve.Detailed Analysis
1. Root Cause Analysis ("What is broken?")
When checking whether A2A is enabled inside get_fast_api_app, the application loads the configuration file
agent.jsonfrom each agent directory:This parsing statement fast_api.py:L719-L721 relies on the
jsonmodule.However, later in the same function under a separate conditional block, there is a function-local import fast_api.py:L748:
Because Python resolves variable scope at compile time, any assignment or import of a name inside a function scope declares that name as local to the entire function scope. Thus,
jsonis treated as local throughoutget_fast_api_app. Since the execution path reachesjson.load(f)before execution reaches theimport jsonstatement at line 748, Python raises:UnboundLocalError: cannot access local variable 'json' where it is not associated with a valueSince fast_api.py:L19 already performs a global import of
json:The local import on line 748 is redundant and directly causes this runtime error.
2. Existing Pull Requests ("Is there a linked PR that fixes this issue?")
5994or the associatedUnboundLocalErrorerror.3. Recommendation
404 Not Foundfor any A2A related endpoints (likeagent-card.json). Standard developer workflows using A2A are broken ingoogle-adk==2.2.0.import jsonstatement on line 748 of fast_api.py resolves the scope binding issue.jsonmodule is already safely imported at the top-level of fast_api.py:L19, guaranteeing its availability.surajksharma07 commentedon Jun 8, 2026
@ufJmacca This is a Python scoping bug that snuck into 2.2.0: the import json inside get_fast_api_app() (~line 748) causes Python to treat json as local for the entire function, so the earlier json.load() in the A2A setup hits UnboundLocalError before any routes get registered.
As a quick workaround, try removing that inner import json line — the module-level import at line 19 already covers it.
Could you check whether the A2A agent card endpoint comes back correctly on your end after that?
If it resolves the 404s, please do a thorough test pass before raising a PR — happy to take a look once you've confirmed it's working!
ufJmacca commentedon Jun 8, 2026
Thanks @surajksharma07.
I can see that someone else has already linked a PR for this issue, so it looks like there is already a path forward.
To be clear, I am more than capable of submitting the same fix myself. The root cause and suggested patch were already included in the issue description.
However, I am not going to open a PR for this one. My previous attempt to contribute to this project was not acknowledged in a way I consider acceptable: my PR was effectively taken and resubmitted under a maintainer’s own account, without a thank you, explanation, or any clear reason why it was handled that way.
Given that experience, I am happy to report issues with clear reproduction steps, root cause analysis, and suggested fixes, but I am not comfortable submitting PRs to this project unless contributor attribution and communication improve.
surajksharma07 commentedon Jun 8, 2026
@ufJmacca That's a fair concern and worth raising openly — attribution matters.
Your issue report genuinely did the heavy lifting: you pinpointed the exact file (cli/fast_api.py), the exact function (get_fast_api_app), the Python scoping mechanism behind it, and the one-line fix, all in the original description.
PR #6000 by @he-yufeng implements exactly that removal (dropping the redundant import json inside the function) and adds a focused regression test (test_a2a_agent_json_registers_routes) that verifies A2A routes are correctly registered after an agent.json is processed — it's a clean, minimal fix directly off your analysis.
That PR is currently open and pending review on google:main.
In the meantime:
Downgrade: pip install "google-adk==2.1.0" — the scoping bug was introduced in 2.2.0.
Manual patch: in your local install, find get_fast_api_app() in cli/fast_api.py and remove the import json line inside the function body (~line 748).
The module-level import json at line 19 already covers it.
Will follow up here once PR #6000 merges and a patch release is out.
Team is looking more into it.
Thanks!
ftnext commentedon Jun 19, 2026
We're also affected by this issue and have temporarily downgraded to
google-adk==2.1.0as a workaround.https://tech.uzabase.com/entry/2026/06/19/101024 (Japanese)
I’d like to use A2A with the latest ADK version, so I'm hoping PR #6000 can be reviewed and merged soon.
Thanks for looking into this.
surajksharma07 commentedon Jul 13, 2026
Thanks for confirming this @ftnext — good news: checked the latest main and the redundant import json is already gone, just not via PR #6000. It got removed as a side effect of an unrelated refactor (38587d7, the a2a-sdk compatibility shim switch, merged Jul 9), so A2A route registration works again on unreleased main.
It's still broken in every published release through v2.4.0 though, so keep pinning to 2.1.0 until the next release ships.
PR #6000's actual import fix is now redundant on top of main, but its regression test is still worth landing — flagging that to the reviewers so it doesn't get merged blind against a stale diff!
Team is looking more into it.
Thanks!
ftnext commentedon Jul 13, 2026
@surajksharma07 Thank you for the update and for confirming that the fix is now present on main.
Frankly speaking, given that the fix itself amounts to removing a single redundant import line, it feels like this regression has remained unresolved in published releases for far too long. A2A route registration has been broken in every release from v2.2.0 through v2.4.0, and we have to pin to v2.1.0.
Could the team please commit to shipping this fix in the next release and confirm that here?
The regression test from PR #6000 is certainly valuable for preventing a recurrence. However, from an ADK user’s perspective, landing that test primarily supports the project’s development and maintenance process, whereas making the already-fixed functionality available in a published release directly affects users. I therefore do not think incorporating the test should delay shipping the fix.
Thank you again for looking into this. A clear commitment that the fix will be included in the next release would be greatly appreciated.
ftnext commentedon Jul 16, 2026
Fixed at v2.5.0 (38587d7f) (not documented in release note)
Thanks
surajksharma07 commentedon Jul 17, 2026
Confirmed — checked the v2.5.0 changelog directly and 38587d7 genuinely isn't in it.
That's not an oversight in the sense of someone forgetting to write a line; it's structural: that commit is tagged refactor: and this repo's changelog generator only pulls feat:/fix: commits into the Features/Bug Fixes sections, so a fix landing as a side effect of a refactor was never going to surface there.
Good catch regardless, and worth knowing for future regressions that land the same way.
PR #6000's actual line-removal diff is moot since that import is already gone on main, but its regression test is still worth having so this can't silently regress again — @he-yufeng if you're up for trimming #6000 down to just test_a2a_agent_json_registers_routes, that's still mergeable on its own.
Marking this fixed as of 2.5.0 — anyone still on 2.2.0–2.4.0 should upgrade rather than keep pinning to 2.1.0!