aaif-goose / aaif-goose/goose

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

Aberta
#11,823 2 comentários 0 reações 1 responsável Reivindicada por @filipkujawa Ver no GitHub
Linguagem predominante
Rust
Estrelas
54.2k
Forks
6.2k
Merge médio
3d 2h
PRs com merge (30d)
262

Descrição

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.

Guia de contribuição

Abrir o guia de contribuição

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.