Tracer Version(s)
4.13.1 (also 4.14.0rc4, and every release back to 3.15.0 — see below)
Python Version(s)
Python 3.13.13
Pip Version(s)
pip 26.2.1
Bug Report
The google_adk integration wraps a private function that google-adk 2.7.0 removed, and it wraps it without a guard. Any app that patches google_adk with raise_errors=True — which is what LLMObs.enable() does — crashes at patch time.
google-adk 2.7.0 (published to PyPI on 13 Aug 2026) dropped __call_tool_live from google/adk/flows/llm_flows/functions.py. The live path now runs through __call_tool_async. ddtrace/contrib/internal/google_adk/patch.py still does this:
# Tool execution (central dispatch)
wrap("google.adk", "flows.llm_flows.functions.__call_tool_async", _traced_functions_call_tool_async)
wrap("google.adk", "flows.llm_flows.functions.__call_tool_live", _traced_functions_call_tool_live)
The second line raises AttributeError. unpatch() has the same problem on the matching unwrap call.
The code executors a few lines below already handle this correctly — they check check_module_path(...) before wrapping. The two tool-dispatch wraps just need the same treatment.
Why this is worse than losing traces. LLMObs.enable() calls patch(raise_errors=True, ...), so the AttributeError escapes into the caller. In our case that was an import-time call in a library, so the exception propagated out of module import and killed the process. Every agent deploy in our fleet failed within hours of google-adk 2.7.0 landing, and nothing in our own code had changed. Under ddtrace-run the failure is swallowed instead, so the same pairing silently runs with LLM Observability switched off and only an ERROR line at container start to show for it.
Affected versions. I checked the file at each tag. __call_tool_live is wrapped without a guard in 3.15.0 (the first release with the google_adk integration) through 4.13.1, and it is still there on main and in 4.14.0rc4. There is no ddtrace version a user can move to instead — the only workaround is to pin google-adk<2.7.0.
Workaround we are running. We alias the old name back on before enabling LLMObs, so the wrap finds something:
from google.adk.flows.llm_flows import functions
if not hasattr(functions, "__call_tool_live"):
functions.__call_tool_live = functions.__call_tool_async
Nothing in google-adk 2.7.0 calls the alias, so the tracer attached to it never fires — the live tool spans are simply gone. This is a stopgap and we would like to drop it.
Suggested fix. Guard both wraps (and both unwraps) the same way the code executors are guarded, and route live tool calls through whichever symbol the installed google-adk actually exports.
Reproduction Code
With google-adk==2.7.0 and ddtrace==4.13.1 installed:
import google.adk
import ddtrace
ddtrace.patch(raise_errors=True, google_adk=True)
Import order matters for when it fails, not whether. The wrap is registered as a post-import hook, so if google.adk has not been imported yet the same AttributeError is raised later, at the first import google.adk.
The real-world trigger is the same call made underneath LLMObs.enable().
Error Logs
Traceback (most recent call last):
File "<string>", line 3, in <module>
ddtrace.patch(raise_errors=True, google_adk=True)
File ".../ddtrace/_monkey.py", line 391, in patch
when_imported(module)(
File ".../wrapt/importer.py", line 307, in register
register_post_import_hook(hook, name)
File ".../wrapt/importer.py", line 87, in register_post_import_hook
hook(module)
File ".../ddtrace/_monkey.py", line 285, in on_import
imported_module.patch()
File ".../ddtrace/contrib/internal/google_adk/patch.py", line 228, in patch
wrap("google.adk", "flows.llm_flows.functions.__call_tool_live", _traced_functions_call_tool_live)
File ".../wrapt/patches.py", line 209, in wrap_function_wrapper
return wrap_object(target, name, FunctionWrapper, (wrapper,))
File ".../wrapt/patches.py", line 90, in wrap_object
(parent, attribute, original) = resolve_path(target, name)
File ".../wrapt/patches.py", line 59, in resolve_path
original = lookup_attribute(parent, attribute)
File ".../wrapt/patches.py", line 53, in lookup_attribute
return getattr(parent, attribute)
AttributeError: module 'google.adk.flows.llm_flows.functions' has no attribute '__call_tool_live'. Did you mean: '__call_tool_async'?
Confirming the symbol is gone:
>>> from google.adk.flows.llm_flows import functions
>>> hasattr(functions, "__call_tool_live")
False
>>> hasattr(functions, "__call_tool_async")
True
Libraries in Use
ddtrace==4.13.1
google-adk==2.7.0
wrapt==2.3.0
google-adk 2.6.3 exports both __call_tool_live and __call_tool_async; 2.7.0 exports only __call_tool_async.
Operating System
Darwin 25.6.0 (arm64). Also seen on Linux in Cloud Run and Vertex AI Agent Engine.
Tracer Version(s)
4.13.1 (also 4.14.0rc4, and every release back to 3.15.0 — see below)
Python Version(s)
Python 3.13.13
Pip Version(s)
pip 26.2.1
Bug Report
The
google_adkintegration wraps a private function thatgoogle-adk2.7.0 removed, and it wraps it without a guard. Any app that patchesgoogle_adkwithraise_errors=True— which is whatLLMObs.enable()does — crashes at patch time.google-adk2.7.0 (published to PyPI on 13 Aug 2026) dropped__call_tool_livefromgoogle/adk/flows/llm_flows/functions.py. The live path now runs through__call_tool_async.ddtrace/contrib/internal/google_adk/patch.pystill does this:The second line raises
AttributeError.unpatch()has the same problem on the matchingunwrapcall.The code executors a few lines below already handle this correctly — they check
check_module_path(...)before wrapping. The two tool-dispatch wraps just need the same treatment.Why this is worse than losing traces.
LLMObs.enable()callspatch(raise_errors=True, ...), so theAttributeErrorescapes into the caller. In our case that was an import-time call in a library, so the exception propagated out of module import and killed the process. Every agent deploy in our fleet failed within hours ofgoogle-adk2.7.0 landing, and nothing in our own code had changed. Underddtrace-runthe failure is swallowed instead, so the same pairing silently runs with LLM Observability switched off and only an ERROR line at container start to show for it.Affected versions. I checked the file at each tag.
__call_tool_liveis wrapped without a guard in 3.15.0 (the first release with thegoogle_adkintegration) through 4.13.1, and it is still there onmainand in 4.14.0rc4. There is no ddtrace version a user can move to instead — the only workaround is to pingoogle-adk<2.7.0.Workaround we are running. We alias the old name back on before enabling LLMObs, so the wrap finds something:
Nothing in
google-adk2.7.0 calls the alias, so the tracer attached to it never fires — the live tool spans are simply gone. This is a stopgap and we would like to drop it.Suggested fix. Guard both wraps (and both unwraps) the same way the code executors are guarded, and route live tool calls through whichever symbol the installed
google-adkactually exports.Reproduction Code
With
google-adk==2.7.0andddtrace==4.13.1installed:Import order matters for when it fails, not whether. The wrap is registered as a post-import hook, so if
google.adkhas not been imported yet the sameAttributeErroris raised later, at the firstimport google.adk.The real-world trigger is the same call made underneath
LLMObs.enable().Error Logs
Confirming the symbol is gone:
Libraries in Use
google-adk2.6.3 exports both__call_tool_liveand__call_tool_async; 2.7.0 exports only__call_tool_async.Operating System
Darwin 25.6.0 (arm64). Also seen on Linux in Cloud Run and Vertex AI Agent Engine.