aaif-goose / aaif-goose/goose

Duplicate tool-call ids corrupt function names and permanently kill a Gemini/Vertex session

Abierto
#11,823 2 comentarios 0 reacciones 1 asignado Reclamado por @filipkujawa Ver en GitHub
Lenguaje dominante
Rust
Estrellas
54.2k
Forks
6.2k
Merge medio
3 d 4 h
PR fusionados (30 d)
240

Descripción

When Gemini reuses a tool-call id within a session, goose serialises a `functionResponse` under the wrong function name. Vertex rejects the request with `400 Request contains an invalid argument`, and because the ids are persisted the session is dead permanently — every later turn replays the same corrupt payload.

Line numbers are v1.50.0; the code is unchanged since at least v1.45.0.

### Mechanism

`crates/goose-provider-types/src/formats/google.rs:94` builds a name lookup keyed on the tool-call id, collected from **all** messages up front:

```rust
let tool_names: HashMap<_, _> = filtered.iter()
.flat_map(|message| &message.content)
.filter_map(|content| match content {
MessageContentBlock::ToolRequest(request) => request.tool_call.as_ref().ok()
.map(|tool_call| (request.id.as_str(), sanitize_function_name(&tool_call.name))),
_ => None })
.collect();
```

`:224` and `:236` resolve each response's name from it. If two requests share an id, the later overwrites the earlier, so **both** responses go out under the later call's name.

The ids are the provider's, not goose's: `:347` takes `functionCall.id` from the response and only falls back to `Uuid::new_v4()` when it is absent. `crates/goose/src/providers/formats/gcpvertexai.rs:309` routes Vertex through this parser.

### Evidence

From a session that died this way (`gcp_vertex_ai`, `gemini-3.5-flash`): `call_3335` was issued for `sugar_filter_records`, then reused 18 messages later for `sugar_get_module_fields`. Replaying that session's ten tool calls through the current logic mislabels the first response; the outgoing request then carries a `functionResponse` naming a tool that call never invoked.

Across 661 sessions and ~11,800 tool-call requests, 8 sessions carry a reused id and every one is fatal.

### Impact

The failure is disguised. goose retries at the model's known location, and the surfaced error is `error sending request for url`, so it reads as a network fault. `grep -c "Trying known location"` in the goose logs is a reliable tell.

Recovery needs a hand-edit of `sessions.db` — stop the agent, back it up, and rename the later of the two colliding pairs (the request row and its response row):

```sql
update messages set content_json = replace(content_json,'call_XXXX','call_XXXXb')
where id in (, );
```

### Suggested fix

Fill `tool_names` as the conversation is walked rather than up front, so a response resolves against the most recent *preceding* request carrying its id. That leaves provider ids untouched and is behaviour-neutral when they are unique. Patch and test below; happy to open a PR.

Guía de contribución

Abrir la guía de contribución

Línea de trabajo

El error está en `crates/goose-provider-types/src/formats/google.rs`, en las líneas 94 y 224/236. Empieza leyendo la construcción del HashMap `tool_names` y la resolución de nombres de las respuestas. Comprende el flujo de la sesión en `crates/goose/src/providers/formats/gcpvertexai.rs`. Una prueba debe simular una sesión con ids de llamadas a herramientas duplicados y verificar que las respuestas tengan los nombres correctos. 'Done' significa que el fix supera las pruebas existentes y una nueva prueba de regresión.

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

Evaluación

Stack tecnológico
rust
Área
ai-infra-agents
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Activo
Claridad
Bien especificado
Aptitud para principiantes
65/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.