Beschreibung
Wenn Googles Gemini-API mitten in einer gestreamten Antwort fehlschlägt, behandelt Discourse AI die Antwort als vollständig. Wenn die KI-Übersetzung aktiviert ist, wird der vor dem Fehlschlag empfangene Fragment als fertige Übersetzung des Beitrags oder des Thementitels gespeichert. Es wird kein Fehler protokolliert und der Backfill versucht das Element nicht erneut, da nun eine Übersetzungszeile existiert.
Beobachtet auf einer selbst gehosteten Site, die release/2026.7 (2026.7.3, Commit f1caa6321287f918644fba9ff8943579fdcf027a) mit dem mitgelieferten discourse-ai-Plugin ausführt. Das LLM ist gemini-3.8-flash über den Google-Provider, wobei der Service-Tier auf flex gesetzt ist. Flex ist der Tier, bei dem Google Last abwirft, daher erzeugt er regelmäßig diese Mid-Stream-Fehler; die unten beschriebene Behandlung hängt nicht vom Tier ab.
In diesen Fällen antwortet Google mit HTTP 200, streamt ein oder mehrere normale Ereignisse und sendet dann ein Fehlerereignis im selben Stream:
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"}}
Das Audit-Log protokolliert response_status 200 und eine kleine response_tokens-Zählung. Beispiele für das Gespeicherte: Ein 917 Zeichen langer Beitrag wurde als 26 Zeichen gespeichert (nur die Begrüßungszile), nur-Link-Beiträge wurden als die ersten 8 bis 24 Zeichen der URL gespeichert und Thementitel wurden mitten im Wort abgeschnitten.
Auf dieser Site gemessen: 23 von 394 beantworteten Flex-Tier-Übersetzungsaufforderungen (5,8 %) endeten auf diese Weise. Keine der 952 Standard-Tier-Aufforderungen an dasselbe Modell tat dies.
Ursache
DiscourseAi::Completions::Endpoints::Gemini#decode_chunkliest nurcandidatesaus jedem geparsten Stream-Ereignis. Ein Ereignis, dessen oberste Ebene den Schlüsselerrorhat, liefert keine Teile und wird ohne jegliche Prüfung übersprungen.DiscourseAi::Completions::Endpoints::Baseentscheidet zwischen Erfolg, Wiederholung und Fehlschlag ausschließlich anhand des HTTP-Status (response.code.to_i != 200). Der Status ist hier 200, daher wird die vorhandene Retry-Logik für 503 nie erreicht.- Der Stream endet dann normal und der Aufrufer erhält den bis dahin akkumulierten Text.
DiscourseAi::Translation::PostLocalizerundTopicLocalizerspeichern ihn.
Derselbe Code ist auf main Stand 2026-10-03 vorhanden.
Vorschlag zur Behebung
Ein error-Objekt auf oberster Ebene in einem gestreamten Gemini-Ereignis als fehlgeschlagene Completion behandeln: den teilweisen Output verwerfen und CompletionFailed auslösen, damit die vorhandene Retry-Verarbeitung auf wiederholbare Codes wie 503 angewendet wird und Aufrufer nie ein Fragment als vollständige Antwort erhalten.
Reproduzieren
Der Fehler hängt von der Last bei Google ab und kann daher nicht erzwungen werden. Auf einer Site, die ein Gemini-Modell im Flex-Tier mit KI-Übersetzung verwendet, können betroffene Aufrufe nachträglich im Audit-Log gefunden werden:
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
Jede Zeile ist ein Aufruf, der HTTP 200 zurückgab, eine Ausgabe produzierte und dann ein Fehlerereignis trug. Die entsprechenden Zeilen in post_localizations oder topic_localizations enthalten den abgeschnittenen Text.
Workaround
Das Verschieben der Übersetzungsagenten auf den Standard-Service-Tier stoppte neue Vorkommnisse. Die gespeicherten Fragmente mussten mit der obigen Abfrage gefunden, gelöscht und erneut übersetzt werden.