Skip to content

[BUG]: google_adk patch raises AttributeError on google-adk >= 2.7.0 (__call_tool_live was removed) #19776

Description

@rynecheow

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.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions