Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

AdkApp.async_stream_query(session_events=...) always raises AttributeError, and fails as a silent HTTP 200 on a deployed Agent Engine

Abierto
#7,118 3 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
52/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
python

Línea de trabajo

Empieza en vertexai/agent_engines/templates/adk.py, en async_stream_query, y compara su ruta session_events con streaming_agent_run_with_events alrededor de las líneas 1364 y 1393. Lee set_up() y el punto de entrada desplegado google/adk/cli/fast_api.py para entender qué atributos en memoria están disponibles. Se considera terminado cuando la reproducción de los eventos proporcionados funciona dentro del proceso y en un Agent Engine desplegado sin un AttributeError, una sesión huérfana ni un flujo de respuesta vacío.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

api: vertex-ai

Summary

AdkApp.async_stream_query(session_events=...) always fails, because async_create_session() returns a serialized dict and that dict is passed straight to BaseSessionService.append_event(session=...), which requires a Session.

On a deployed Agent Engine this fails silently: the engine answers HTTP 200 with an empty response stream. No error event, no error status, nothing on the wire. The traceback only appears in Cloud Logging. Every caller of session_events therefore sees what looks like a successful turn where the agent had nothing to say.

Environment

  • google-cloud-aiplatform 1.163.0 and 2.0.1 (current release; the bug is in both)
  • google-adk 2.6.3
  • Python 3.12, and Python 3.11 in the deployed container

Cause

In vertexai/agent_engines/templates/adk.py:

if not session_id:
    session = await self.async_create_session(user_id=user_id)   # returns self._serialize(session) -> dict
    session_id = session["id"]
    if session_events is not None:
        ...
        for event in session_events:
            if not isinstance(event, Event):
                event = Event.model_validate(event)
            await session_service.append_event(
                session=session,     # <-- dict, but append_event needs a Session
                event=event,
            )
  • adk.py:1182 in 1.163.0
  • adk.py:1229 in 2.0.1

async_create_session ends with return self._serialize(session) (1.163.0 adk.py:1591), so the value bound to session is a plain dict. That is correct for the method's own contract, since it has to be JSON-serializable for the reasoning-engine wire protocol. It is just not what append_event accepts.

Two different AttributeErrors result, depending on the session service, which makes this look like two unrelated bugs:

  • InMemorySessionService (in-process): AttributeError: 'dict' object has no attribute 'app_name' (in_memory_session_service.py:326)
  • VertexAiSessionService (deployed engine): AttributeError: 'dict' object has no attribute 'events' (vertex_ai_session_service.py:387 -> base_session_service.py:164)

Note that session_events=[] does not reproduce it: the loop body never runs. At least one event is needed.

Reproduction

No credentials, no project and no deployment needed. This fails before any model or API call:

import asyncio

from google.adk.agents import Agent
from vertexai.agent_engines.templates.adk import AdkApp

app = AdkApp(agent=Agent(model="gemini-2.5-flash", name="repro"))

PRIOR = [
    {
        "id": "e1",
        "invocation_id": "i1",
        "author": "user",
        "timestamp": 1.0,
        "content": {"role": "user", "parts": [{"text": "my name is Ada"}]},
    }
]


async def main():
    async for event in app.async_stream_query(
        message="what is my name?", user_id="u", session_events=PRIOR
    ):
        print(event)


asyncio.run(main())
File ".../vertexai/agent_engines/templates/adk.py", line 1182, in async_stream_query
    await session_service.append_event(
File ".../google/adk/sessions/in_memory_session_service.py", line 326, in append_event
    app_name = session.app_name
AttributeError: 'dict' object has no attribute 'app_name'

Deployed to Agent Engine, the same call over :streamQuery returns HTTP 200 with an empty body, and Cloud Logging shows the 'dict' object has no attribute 'events' variant.

Expected

session_events initializes the new session with the supplied events, and the query then runs against that history. That is what the docstring promises: "The session events to use for the query. This will be used to initialize the session if session_id is not provided."

Suggested fix

Updated 2026-08-31 — the original suggestion (re-fetch the Session before appending) fixes the crash only. Kept below because my comment about #7119 refers to it.

Mirror streaming_agent_run_with_events, which faces the same "no session id, initialize from supplied events" case and already handles it: in_memory_session_service and in_memory_runner rather than the managed services, with delete_session in a finally (adk.py:1364, :1393; both attrs populated by set_up() at :1091, :1094).

This fixes the crash, the orphaned session (#7119), and the per-event latency in one change. InMemorySessionService.create_session returns a real Session, so there is no dict to re-fetch; nothing is written to the managed store, so there is no session to leak; and appends against a process-local service are not round trips, so replay stops scaling with transcript length. Verified on a deployed engine in europe-west1.

Implementation constraint: _tmpl_attrs["in_memory_*"] cannot be read directly, because adk deploy agent_engine (google-adk 2.6.3) runs adk api_server, which builds its own AdkApp (google/adk/cli/fast_api.py:760) and sets _tmpl_attrs["runner"] itself, so set_up() never runs and those attrs are absent. The existing if not self._tmpl_attrs.get("runner"): self.set_up() guard does not fire either, since runner is set. Reading them would pass in-process and AttributeError on None in the deployed server — the same silent failure as this issue.

Original suggestion (fixes the crash only)

Re-fetch the Session object before appending, keeping async_create_session's serialized return value as it is:

created = await self.async_create_session(user_id=user_id)
session_id = created["id"]
if session_events is not None:
    session = await session_service.get_session(
        app_name=self._app_name(), user_id=user_id, session_id=session_id
    )
    for event in session_events:
        if not isinstance(event, Event):
            event = Event.model_validate(event)
        await session_service.append_event(session=session, event=event)

I verified this in an AdkApp subclass, in-process and on a deployed engine in europe-west1. With it, a caller-held transcript replays correctly and the agent continues the conversation, including transcripts recorded in an earlier session.

Two adjacent things worth considering while this is open:

  1. The silent failure is the more serious half. An exception raised inside the streaming generator becomes an empty 200 response with no error payload. Even after this bug is fixed, a caller has no way to distinguish "the agent produced no events" from "user code raised". Surfacing an error event, or a non-200, would prevent a whole class of undiagnosable failures.
  2. append_event per event is O(n) round trips against VertexAiSessionService. Replaying an 11-event transcript to a deployed engine added roughly 4.5s to time-to-first-event over the session_id path, about 0.4s per event, which makes session_events impractical for long conversations even once it works. Updated: no bulk-append path is needed — the fix above removes the round trips rather than batching them.
Lenguaje dominante
Python
Estrellas
907
Forks
467
Merge medio
1 d 8 h
PR fusionados (30 d)
40

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de googleapis/python-aiplatform

Todos los issues de googleapis/python-aiplatform

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.