Hmbown/CodeWhale · error

Gemini model `{model}` requires a thought signature to repla

Error message

Gemini model `{model}` requires a thought signature to replay tool call `{id}`, but none was captured (the turn predates signature capture, or the provider omitted it). Start a new session before using tools on this route.

What it means

Before replaying a session to a Gemini route that requires thought signatures (Gemini 2.x thinking models with function calling), the client walks every historical message's `tool_calls` and requires each call to carry `extra_content.google.thought_signature`. A missing signature aborts the request, because the provider would reject the replayed turn; the message tells you to start a new session.

Source

Thrown at crates/tui/src/client/chat.rs:666

    messages: &[Value],
) -> Result<()> {
    if !is_exact_google_chat_route(provider, base_url)
        || !google_model_requires_thought_signatures(model)
    {
        return Ok(());
    }
    for message in messages {
        let Some(tool_calls) = message.get("tool_calls").and_then(Value::as_array) else {
            continue;
        };
        for call in tool_calls {
            let missing = call
                .pointer("/extra_content/google/thought_signature")
                .and_then(Value::as_str)
                .is_none();
            if missing {
                let id = call.get("id").and_then(Value::as_str).unwrap_or("?");
                anyhow::bail!(
                    "Gemini model `{model}` requires a thought signature to replay tool call \
                     `{id}`, but none was captured (the turn predates signature capture, or \
                     the provider omitted it). Start a new session before using tools on \
                     this route."
                );
            }
        }
    }
    Ok(())
}

/// Captured Google signatures ride on tool calls as
/// `extra_content.google.thought_signature`. Only the exact Google route
/// may carry them on the wire; every other provider gets them stripped so
/// a route switch never leaks Google-only fields to a foreign gateway.
fn strip_google_tool_call_extra_content(messages: &mut [Value]) {
    for message in messages {
        let Some(tool_calls) = message.get_mut("tool_calls").and_then(Value::as_array_mut) else {

View on GitHub (pinned to 8880682c63)

Solutions

  1. Start a new session before using tools on this Gemini route, exactly as the message instructs
  2. Re-issue the tool call in a fresh turn so a new signature is captured and stored
  3. Avoid switching models mid-session when the session contains tool calls
  4. Stay on a route/model that does not require thought signatures for existing sessions
Defensive patterns

Strategy: validation

Validate before calling

fn tool_calls_have_thought_signatures(messages: &[serde_json::Value]) -> bool {
    messages.iter().all(|m| {
        m.get("tool_calls")
            .and_then(|t| t.as_array())
            .map(|calls| {
                calls.iter().all(|c| {
                    c.pointer("/extra_content/google/thought_signature")
                        .and_then(|s| s.as_str())
                        .is_some()
                })
            })
            .unwrap_or(true)
    })
}

// Run before switching an existing session to a signature-requiring Gemini route:
if route_requires_thought_signatures(model) && !tool_calls_have_thought_signatures(&history) {
    start_new_session();
}

Type guard

fn tool_calls_have_thought_signatures(messages: &[serde_json::Value]) -> bool {
    messages.iter().all(|m| {
        m.get("tool_calls")
            .and_then(|t| t.as_array())
            .map(|calls| {
                calls.iter().all(|c| {
                    c.pointer("/extra_content/google/thought_signature")
                        .and_then(|s| s.as_str())
                        .is_some()
                })
            })
            .unwrap_or(true)
    })
}

Try / catch

match client.send(request).await {
    Err(e) if e.to_string().contains("requires a thought signature") => {
        // History cannot be replayed on this route; do not retry the same history
        start_new_session_and_replay_user_intent().await;
    }
    result => result,
}

Prevention

When it happens

Trigger: Resuming a session whose tool-call turns predate signature capture; switching an existing tool-using session to a signature-requiring Gemini model mid-conversation; the provider omitted the signature in an earlier response so it was never stored.

Common situations: Upgrading the app and reopening old sessions; changing the model/route on a long tool-using session; provider-side inconsistency in attaching signatures.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/4f8fcfdb1dae79f8. Report an issue: GitHub.