Description
When Google’s Gemini API refuses a request with promptFeedback.blockReason, Discourse AI handles the response as a successful but empty reply. In AI translation this has two effects: the topic or post never receives a detected language, so it is never translated, and the backfill sends the same blocked request again on later runs. Nothing indicates to an administrator that a block occurred.
Observed on a self-hosted site running release/2026.7 (2026.7.3, commit f1caa6321287f918644fba9ff8943579fdcf027a) with the bundled discourse-ai plugin. The request was the language detection of a topic title, using the built-in locale detector agent on gemini-3.5-flash-lite. The title was a short, harmless sentence in Swedish. The same title was translated by gemini-3.8-flash without a block.
Google answered with HTTP 200 and a single stream event with no candidates:
data: {"promptFeedback": {"blockReason": "PROHIBITED_CONTENT"},"usageMetadata": {"promptTokenCount": 417,"totalTokenCount": 417,"promptTokensDetails": [{"modality": "TEXT","tokenCount": 417}],"serviceTier": "standard"},"modelVersion": "gemini-3.5-flash-lite","responseId": "..."}
The request already carries BLOCK_NONE for the four safety categories that Discourse AI sets, so this block is not one those settings influence. The audit log records response_status 200.
Root cause
DiscourseAi::Completions::Endpoints::Gemini#decode_chunkreads onlycandidatesfrom each stream event.promptFeedbackis never inspected, so a blocked request produces an empty result with no error.DiscourseAi::Translation::LanguageDetector#detectreturnsnilwhen the reply does not match its language-tag pattern, which is the case for an empty reply. The topic or post keepslocaleNULL.Jobs::TopicsLocaleDetectionBackfillselects topics withlocaleNULL on every run and has no attempt limit, so by reading the code the blocked request is repeated every five minutes for as long as the topic is in the backfill window. Posts are limited to two detection attempts a day by the relocalize quota, and are retried each day.- Translation candidates require a detected locale, so the item is never translated.
The same code is present on main as of 2026-10-03.
Suggested fix
- Detect
promptFeedback.blockReasonin the Gemini endpoint and raise or log a distinct, visible error that names the reason. - Record the failure against the topic or post so the backfill stops repeating a request that will be blocked again, or apply an attempt limit to topic detection as already exists for posts.
- Surface blocked items on the translation admin page, so an administrator can set the language by hand.
Reproduce
The block depends on Google’s filter and cannot be forced with a known input. Affected calls can be found in the audit log:
SELECT id, created_at, language_model, post_id, topic_id, response_status
FROM ai_api_audit_logs
WHERE feature_name = 'translation'
AND raw_response_payload LIKE '%blockReason%'
ORDER BY created_at
Workaround
Setting the topic’s language by hand lets translation proceed. On this site the translation model did not block the same text.