fix: stop handing exception text to remote A2A peers and trigger callers - #1388
Open
copybara-service[bot] wants to merge 1 commit into
Open
fix: stop handing exception text to remote A2A peers and trigger callers#1388copybara-service[bot] wants to merge 1 commit into
copybara-service[bot] wants to merge 1 commit into
Conversation
An exception message is written for whoever reads the log, not for whoever sent the request. Both remote-facing surfaces were putting it in the response anyway: the A2A executors built the failed TaskStatus message out of `str(exception)`, the /trigger/* handlers interpolated it into the HTTPException detail, and the Java executor used `Throwable#getMessage`. FileNotFoundError, ImportError, pydantic.ValidationError and SDK client errors routinely carry absolute filesystem paths, the layout of the installed packages, environment variable names, configuration values and echoed request payloads. A caller who can reach either endpoint could therefore map the host by provoking failures -- the reconnaissance a path-traversal or enumeration attempt wants, and nothing a legitimate caller can act on. The exception now stays server-side. A new `utils/_error_reporting` helper logs the full traceback next to a short opaque id and returns a fixed summary plus that id, so an operator handed the id from a report lands on the real exception. Every remote-facing handler goes through it: both A2A executors, the Pub/Sub and Eventarc handlers, and the base64 decode path. The retry path needed one more change. `TransientError` was formatting the provider's error text into its own message, so redacting at the boundary alone would have let it out regardless; the cause is chained instead of interpolated. Java gets the same treatment in `AgentExecutor#failedMessage`, which now also owns the logging so the id in the response is the id in the log. `ADK_DEBUG_ERRORS=1` puts the exception text back in for local debugging. It is off by default in both languages, and the summary is a constant at every call site, so nothing interpolated can ride out past the redaction. Each surface is covered by a test asserting that the leaked detail is gone and the correlation id is present, and by one asserting the debug flag restores it. The redaction tests fail against the previous implementation. PiperOrigin-RevId: 957201675
copybara-service
Bot
force-pushed
the
test_957201675
branch
from
July 31, 2026 18:13
a0c5522 to
d1a59d5
Compare
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.
fix: stop handing exception text to remote A2A peers and trigger callers
An exception message is written for whoever reads the log, not for whoever sent
the request. Both remote-facing surfaces were putting it in the response
anyway: the A2A executors built the failed TaskStatus message out of
str(exception), the /trigger/* handlers interpolated it into theHTTPException detail, and the Java executor used
Throwable#getMessage.FileNotFoundError, ImportError, pydantic.ValidationError and SDK client errors
routinely carry absolute filesystem paths, the layout of the installed
packages, environment variable names, configuration values and echoed request
payloads. A caller who can reach either endpoint could therefore map the host
by provoking failures -- the reconnaissance a path-traversal or enumeration
attempt wants, and nothing a legitimate caller can act on.
The exception now stays server-side. A new
utils/_error_reportinghelper logsthe full traceback next to a short opaque id and returns a fixed summary plus
that id, so an operator handed the id from a report lands on the real
exception. Every remote-facing handler goes through it: both A2A executors, the
Pub/Sub and Eventarc handlers, and the base64 decode path.
The retry path needed one more change.
TransientErrorwas formatting theprovider's error text into its own message, so redacting at the boundary alone
would have let it out regardless; the cause is chained instead of interpolated.
Java gets the same treatment in
AgentExecutor#failedMessage, which now alsoowns the logging so the id in the response is the id in the log.
ADK_DEBUG_ERRORS=1puts the exception text back in for local debugging. It isoff by default in both languages, and the summary is a constant at every call
site, so nothing interpolated can ride out past the redaction.
Each surface is covered by a test asserting that the leaked detail is gone and
the correlation id is present, and by one asserting the debug flag restores it.
The redaction tests fail against the previous implementation.