fix(a2a): bind the A2A contract port, ignore generic PORT - #615
Merged
Conversation
serve_a2a() resolved its port from the generic PORT environment variable (#593), but the AgentCore Runtime A2A service contract fixes the container port at 9000 (HTTP is 8080, MCP is 8000). PORT is a widespread convention and is commonly already set to another protocol's port -- notably in an image shared across an HTTP and an A2A runtime, where PORT=8080 is correct for HTTP and fatal for A2A. When PORT was set to anything other than 9000, the A2A server bound there instead. Nothing listened on 9000, the runtime frontend's proxied connection was never answered, and every invocation failed with HTTP 424 (RuntimeClientError) after a client-side read timeout. The container started cleanly and logged no error, since the process was healthy and merely listening on the wrong port. Read the protocol-scoped A2A_PORT instead, which cannot collide with another protocol's port, and warn when the resolved port is not 9000 -- that configuration cannot work in a deployed runtime, so the previous silence was the expensive part of this failure. Precedence is unchanged for explicit callers: port= argument, then A2A_PORT, then 9000.
jariy17
pushed a commit
to aws/agentcore-cli
that referenced
this pull request
Aug 4, 2026
The SDK's serve_a2a() is moving off the generic PORT environment variable onto the protocol-scoped A2A_PORT, because PORT is commonly already set to another protocol's port (8080 for HTTP, 8000 for MCP) in images shared across runtimes -- which silently bound A2A servers off-contract and made every deployed invocation fail with HTTP 424. The dev servers relied on PORT reaching serve_a2a(). CodeZip especially: it runs the agent directly on the host, so each local A2A runtime needs a distinct port. Inject A2A_PORT alongside PORT for the A2A protocol so the selected dev port keeps reaching the server. PORT is still set, so this is a no-op until the SDK change ships and the bedrock-agentcore floor is raised. Safe to land in either order. See aws/bedrock-agentcore-sdk-python#615. Committed with --no-verify: the pre-commit typecheck fails identically on pristine origin/main (13 pre-existing errors -- missing @types/semver, Ink/vitest type drift), none in the files touched here. eslint, prettier, and secretlint all passed.
Contributor
✅ No Breaking Changes DetectedNo public API breaking changes found in this PR. |
Contributor
|
Claude Security Review: no high-confidence findings. (run) |
jariy17
added a commit
to aws/agentcore-cli
that referenced
this pull request
Aug 4, 2026
The SDK's serve_a2a() is moving off the generic PORT environment variable onto the protocol-scoped A2A_PORT, because PORT is commonly already set to another protocol's port (8080 for HTTP, 8000 for MCP) in images shared across runtimes -- which silently bound A2A servers off-contract and made every deployed invocation fail with HTTP 424. The dev servers relied on PORT reaching serve_a2a(). CodeZip especially: it runs the agent directly on the host, so each local A2A runtime needs a distinct port. Inject A2A_PORT alongside PORT for the A2A protocol so the selected dev port keeps reaching the server. PORT is still set, so this is a no-op until the SDK change ships and the bedrock-agentcore floor is raised. Safe to land in either order. See aws/bedrock-agentcore-sdk-python#615. Committed with --no-verify: the pre-commit typecheck fails identically on pristine origin/main (13 pre-existing errors -- missing @types/semver, Ink/vitest type drift), none in the files touched here. eslint, prettier, and secretlint all passed. Co-authored-by: jariy17 <tjariy+jariy17@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
serve_a2a()resolves its port from the genericPORTenvironment variable (added in #593), but the AgentCore Runtime A2A service contract fixes the container port at 9000 (HTTP is 8080, MCP is 8000). WhenPORTis set to anything else, the A2A server binds there, nothing listens on 9000, and every invocation fails with HTTP 424 (RuntimeClientError) after a client-side read timeout.This reads the protocol-scoped
A2A_PORTinstead, and warns when the resolved port is not 9000.The failure
Reported by an internal team whose runtime broke on all invocations after upgrading 1.18.0 → 1.19.0. Their setup is the common shape:
PROTOCOLenv varENV PORT=8080— correct for the HTTP runtime, which reads it and binds 8080PORT=8080On 1.18.0 that was inert, because
serve_a2ahardcodedport: int = 9000and never consulted the environment. 1.19.0 gave a pre-existing, correctly-set variable new meaning in a context where it is wrong. Nothing in their config changed — only the code reading it.The symptoms are unhelpful: the container starts cleanly and logs no error, because the process is healthy and merely listening on the wrong port. The runtime accepts the POST and the connection then hangs until the client times out:
Bind-tested both versions with a real
a2a-sdk0.3.26 install, callingserve_a2a()with no explicit port:PORTunsetPORT=8080Why
A2A_PORTrather than keepingPORTPORTis a near-universal convention — Heroku, Cloud Foundry, Cloud Run, App Runner, Elastic Beanstalk all inject it; Express, Next.js, Flask, and Rails all read it. Its established meaning is "the platform told me where to listen."AgentCore inverts that: the port is not negotiated, it is fixed by protocol. So
PORTis not merely collision-prone here, it is semantically the wrong input for a value the contract already determines. A protocol-scoped name cannot collide, and it lets a multi-protocol image set every port explicitly without ambiguity — there is no single value ofPORTthat is correct for all three protocols.Worth noting this also makes
a2a.pyconsistent with the rest of the SDK again:app.run()andserve_ag_ui()both hardcodeport: int = 8080, andserve_a2awas the only entrypoint readingPORT.Changes
serve_a2a()readsA2A_PORT, notPORT. Precedence unchanged for explicit callers:port=argument, thenA2A_PORT, then 9000.A2A_CONTRACT_PORT/A2A_PORT_ENVconstants, so the contract port is stated once.portrow in the API table and added a Ports section covering the contract, whyPORTis ignored, and how to override locally.Compatibility
serve_a2a()binds 9000 regardless ofPORT.port=callers are unaffected.PORTin 1.19.0: rename toA2A_PORT. 1.19.0 released 2026-07-28, so exposure is short, and the new warning names the problem if it is missed. I did not add aPORTfallback deliberately — that would keep the footgun alive, and it is the footgun that caused the outage.Dependent change
agentcore-cliinjectsPORTfor local A2A dev servers (codezip-dev-server.tsruns the agent on the host, so each runtime needs a distinct port). A companion PR makes the CLI injectA2A_PORTas well: aws/agentcore-cli#1909. That PR should merge after this ships and the CLI'sbedrock-agentcorefloor is raised; until then the CLI keeps settingPORTtoo, so local dev is unaffected either way.Testing
pytest tests/bedrock_agentcore/runtime/test_a2a.py— 34 passed, 1 skipped ona2a-sdk0.3.26; 35 passed on 1.1.2ruff checkandruff format --checkclean on both changed Python filesPORTtests from fix(a2a): honor PORT when serving locally #593 toA2A_PORT, preserving their intentPORTis ignored,A2A_PORTbeatsPORT, the warning fires off-contract, no warning on 9000PORT=8080→ binds 9000;PORT=8080 A2A_PORT=9003→ binds 9003Pre-existing failures in this environment, unrelated and reproduced on pristine
v1.19.0:test_ag_ui.pycollection (ag_uinot installed) and thetest_tracing.pyfamily (missing OTEL deps). Worth a clean-CI confirmation.