{"record":{"id":"46768823ec9f3bc5","repo":"BerriAI/litellm","slug":"content-blocked-keyword-keyword-detected","errorCode":null,"errorMessage":"Content blocked: keyword '{keyword}' detected","messagePattern":"Content blocked: keyword '(.+?)' detected","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py","lineNumber":1346,"sourceCode":"    ) -> str:\n        \"\"\"Handle blocked word match detection and action.\"\"\"\n        verbose_proxy_logger.debug(\"Blocked word '%s' found with action %s\", keyword, action)\n\n        if detections is not None:\n            blocked_word_detection: Final[BlockedWordDetection] = {\n                \"type\": \"blocked_word\",\n                \"keyword\": keyword,\n                \"action\": action.value,\n                \"description\": description,\n            }\n            detections.append(blocked_word_detection)\n\n        if action == ContentFilterAction.BLOCK:\n            error_msg = f\"Content blocked: keyword '{keyword}' detected\"\n            if description:\n                error_msg += f\" ({description})\"\n            verbose_proxy_logger.warning(error_msg)\n            raise HTTPException(\n                status_code=400,\n                detail={\n                    \"error\": error_msg,\n                    \"keyword\": keyword,\n                    \"description\": description,\n                },\n            )\n        elif action == ContentFilterAction.MASK:\n            keyword_pattern_for_masking: Final = self._keyword_to_regex_pattern(keyword)\n            text = re.sub(\n                keyword_pattern_for_masking,\n                self.keyword_redaction_tag,\n                text,\n                flags=re.IGNORECASE,\n            )\n            verbose_proxy_logger.info(\"Masked keyword '%s' in content\", keyword)\n\n        return text","sourceCodeStart":1328,"sourceCodeEnd":1364,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py#L1328-L1364","documentation":"HTTPException(400) raised by _handle_blocked_word_match when a blocked word matches with action BLOCK. Blocked words come from litellm_params.blocked_words or blocked_words_file (each entry: keyword, action, optional description); on BLOCK the whole request is rejected, and the description field is appended to the error message when present. On MASK the keyword would be redacted with keyword_redaction_tag instead.","triggerScenarios":"A guarded request's text contains an exact (case-insensitive) keyword listed in blocked_words/blocked_words_file whose action is BLOCK.","commonSituations":"Compliance word-lists (internal project names, competitor names, profanity) configured with BLOCK; a shared keyword like a common word blocking benign traffic; entries migrated from another tool with the wrong action default.","solutions":["Read the 400 detail: keyword (and description) name the exact blocked word that fired.","If the word should be redacted rather than rejected, change that entry's action from BLOCK to MASK in the words file/config.","Remove or narrow over-broad keywords that produce false positives.","Handle the 400 client-side as a deterministic rejection and rephrase the input."],"exampleFix":"# before — reject any request mentioning the codename\nblocked_words:\n  - keyword: project-x\n    action: BLOCK\n\n# after — redact it instead\nblocked_words:\n  - keyword: project-x\n    action: MASK","handlingStrategy":"try-catch","validationCode":"# Client-side pre-screen against your blocked-words list (case-insensitive)\nBLOCKED = {\"project-x\", \"acme-secret\"}  # keep in sync with blocked_words_file\n\ndef contains_blocked_word(text: str) -> str | None:\n    lowered = text.lower()\n    for w in BLOCKED:\n        if w in lowered:\n            return w\n    return None\n\n# if w := contains_blocked_word(prompt): reject or scrub locally first","typeGuard":"from fastapi import HTTPException\n\ndef is_blocked_word_exception(exc: HTTPException) -> bool:\n    d = exc.detail if isinstance(exc.detail, dict) else {}\n    return exc.status_code == 400 and isinstance(d.get(\"keyword\"), str) and \"keyword\" in str(d.get(\"error\", \"\"))","tryCatchPattern":"try:\n    resp = await client.chat.completions.create(**params)\nexcept HTTPException as e:\n    if is_blocked_word_exception(e):\n        keyword = e.detail[\"keyword\"]\n        raise PolicyRejectedError(\n            user_message=\"Your request contains a blocked term.\",\n            blocked_keyword=keyword,  # internal audit only\n        ) from e\n    raise","preventionTips":["Use action: MASK when terms should be redacted rather than hard-rejected.","Keep the blocked-words list curated — common words as BLOCK entries cause constant false positives.","Mirror the list client-side for a fast pre-screen and a better user message.","Never echo the matched keyword or description to untrusted callers; log it internally."],"tags":["content-moderation","content-filter","keyword","http-400","guardrails"],"backgroundTag":"content-filter-blocked","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}