# Discourse AI: um evento de erro do Gemini em uma resposta em streaming é ignorado e o texto parcial é salvo como uma tradução completa

**URL:** <https://meta.discourse.org/t/discourse-ai-a-gemini-error-event-inside-a-streamed-reply-is-ignored-and-the-partial-text-is-stored-as-a-complete-translation/413854>\
**Category:** Bug\
**Tags:** ai\
**Created:** [Outubro 3, 2026, 4:15pm UTC](https://meta.discourse.org/t/discourse-ai-a-gemini-error-event-inside-a-streamed-reply-is-ignored-and-the-partial-text-is-stored-as-a-complete-translation/413854 "2026-10-03T16:15:59Z")\
**Posts on this page:** 1\
**Showing post:** 1

<div class="post-metadata">

**Author:** ![Sailor](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sailor/32/579029_2.png) [@Sailor](https://meta.discourse.org/u/Sailor)\
**Post date:** [Outubro 3, 2026, 4:15pm UTC](https://meta.discourse.org/t/discourse-ai-a-gemini-error-event-inside-a-streamed-reply-is-ignored-and-the-partial-text-is-stored-as-a-complete-translation/413854/1 "2026-10-03T16:15:59Z")

</div>

## Descrição

Quando a API do Gemini, da Google, falha no meio de uma resposta em streaming, o Discourse AI trata a resposta como concluída. Com a tradução por IA ativada, o fragmento recebido antes da falha é armazenado como a tradução finalizada da publicação ou do título do tópico. Nenhum erro é registrado e o backfill nunca tenta novamente o item, pois uma linha de tradução agora existe.

Observado em um site auto-hospedado executando `release/2026.7` (2026.7.3, commit `f1caa6321287f918644fba9ff8943579fdcf027a`) com o plugin discourse-ai incluído. O LLM é `gemini-3.8-flash` através do provedor Google, com o nível de serviço definido como `flex`. Flex é o nível em que a Google reduz a carga, portanto, produz regularmente essas falhas no meio do streaming; o tratamento descrito abaixo não depende do nível.

Nesses casos, a Google responde com HTTP 200, transmite um ou mais eventos normais e, em seguida, envia um evento de erro no mesmo stream:

```plaintext
data: {"candidates": [...],"usageMetadata": { ... },"serviceTier": "flex","modelVersion": "gemini-3.8-flash","responseId": "..."}

data: {"error": {"code": 503,"message": "This model is currently experiencing high demand. Spikes in demand are usually temporary. Please try again later.","status": "UNAVAILABLE"}}

```

O log de auditoria registra `response_status` 200 e uma pequena contagem de `response_tokens`. Exemplos do que foi armazenado: uma publicação de 917 caracteres salva como 26 caracteres (apenas sua linha de saudação), publicações contendo apenas links salvas como os primeiros 8 a 24 caracteres da URL e títulos de tópicos cortados no meio da palavra.

Medido neste site: 23 de 394 chamadas de tradução respondidas no nível Flex (5,8%) terminaram dessa forma. Nenhuma das 952 chamadas no nível padrão para o mesmo modelo apresentou esse comportamento.

## Causa raiz

1. `DiscourseAi::Completions::Endpoints::Gemini#decode_chunk` lê apenas `candidates` de cada evento de stream analisado. Um evento cuja chave de nível superior é `error` não gera partes e é ignorado sem nenhuma verificação.
2. `DiscourseAi::Completions::Endpoints::Base` decide entre sucesso, nova tentativa e falha com base apenas no status HTTP (`response.code.to_i != 200`). O status é 200 aqui, portanto, a lógica existente de nova tentativa para 503 nunca é acionada.
3. O stream então termina normalmente e o chamador recebe o texto acumulado até o momento. `DiscourseAi::Translation::PostLocalizer` e `TopicLocalizer` o salvam.

O mesmo código está presente no `main` desde 2026-10-03.

## Correção sugerida

Tratar um objeto `error` de nível superior em um evento de streaming do Gemini como uma conclusão falha: descartar a saída parcial e lançar `CompletionFailed`, para que o tratamento existente de novas tentativas se aplique a códigos repetíveis, como 503, e os chamadores nunca recebam um fragmento como uma resposta completa.

## Reproduzir

A falha depende da carga da Google, portanto, não pode ser forçada. Em um site que usa um modelo Gemini no nível Flex com tradução por IA, as chamadas afetadas podem ser encontradas posteriormente no log de auditoria:

```sql
SELECT id, created_at, post_id, topic_id, response_status, response_tokens
FROM ai_api_audit_logs
WHERE feature_name = 'translation'
  AND raw_response_payload LIKE '%data: {"error"%'
  AND response_tokens > 0
ORDER BY created_at

```

Cada linha é uma chamada que retornou HTTP 200, produziu alguma saída e, em seguida, continha um evento de erro. As linhas correspondentes em `post_localizations` ou `topic_localizations` contêm o texto truncado.

## Solução temporária

Mover os agentes de tradução para o nível de serviço padrão impediu novas ocorrências. Os fragmentos armazenados tiveram que ser encontrados com a consulta acima, excluídos e traduzidos novamente.

---

_[View the full topic](https://meta.discourse.org/t/discourse-ai-a-gemini-error-event-inside-a-streamed-reply-is-ignored-and-the-partial-text-is-stored-as-a-complete-translation/413854)._
