Skip to content

BuiltInCodeExecutor emits an empty final event before every model reply #7878

Description

@adityasingh2400

🔴 Required Information

Describe the Bug:
With code_executor=BuiltInCodeExecutor(), every model reply is preceded by an empty event. That event has no content and no actions, but is_final_response() returns True for it, and it's stored in the session.

The built-in branch of the code execution response processor (flows/llm_flows/extensions/_code_execution.py) always yields an Event(actions=event_actions). It does so even when the response had no image to save as an artifact, in which case event_actions is empty.

is_final_response() documents that "Application and UI layers can rely on this helper to detect a complete, user-facing response". So a run loop that stops at the first final response returns nothing. This is what #4138 ran into: "only get a empty data event when running in my custom runner". The reporter closed it by no longer stopping at is_final_response(), but the empty event is still emitted on main.

Steps to Reproduce:

  1. Create an LlmAgent with code_executor=BuiltInCodeExecutor().
  2. Have the model answer with plain text, so there's no image.
  3. Iterate runner.run_async(...) and print each event's content and is_final_response().

Expected Behavior:
Only the model's reply is emitted. An extra event appears only when it carries an artifact_delta for a saved image.

Observed Behavior:

'a' content= None actions= {'state_delta': {}} final= True
'a' content= parts=[Part(text='The answer is 42.')] ... final= True
session events: 3 [('user', True), ('a', False), ('a', True)]

Environment Details:

  • ADK Library Version (pip show google-adk): 2.13.0 (also on main at e9e8f61)
  • Desktop OS: macOS
  • Python Version (python -V): 3.11.16

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: Gemini 2.x / 3 (BuiltInCodeExecutor requires a Gemini model); the repro uses a mock BaseLlm

🟡 Optional Information

Minimal Reproduction Code:

agent = LlmAgent(name="a", model=model, code_executor=BuiltInCodeExecutor())
runner = InMemoryRunner(agent=agent)
session = await runner.session_service.create_session(app_name=runner.app_name, user_id="u")

async for event in runner.run_async(
    user_id="u", session_id=session.id,
    new_message=types.Content(role="user", parts=[types.Part(text="What is 6 * 7?")])):
  print(repr(event.author), "content=", event.content, "final=", event.is_final_response())
  if event.is_final_response():
    break  # stops on the empty event, before the answer

How often has this issue occurred?:

  • Always (100%), for every reply that contains no image

Activity

  1. self-assigned this
    on Nov 2, 2026
  2. added theissue type on Nov 2, 2026
  3. added
    agent engine[Component] This issue is related to Vertex AI Agent Engine
    on Nov 2, 2026
  4. sanketpatil06 commented on Nov 3, 2026

    @sanketpatil06

    Thanks for the report and fix, @adityasingh2400. we confirmed this on main and checked #7879 locally. Text-only replies no longer get the empty final event, and image artifacts are still saved. The regression test fails on main and passes with the fix, and no other tests broke.
    The PR is currently under review and will get merged once approved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

agent engine[Component] This issue is related to Vertex AI Agent Engine

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions