Duplicate tool-call ids corrupt function names and permanently kill a Gemini/Vertex session
- 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
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