{"record":{"id":"932fd1180569ca8f","repo":"BerriAI/litellm","slug":"invalid-format-file-must-contain-blocked-words","errorCode":null,"errorMessage":"Invalid format: file must contain 'blocked_words' key with list of words","messagePattern":"Invalid format: file must contain 'blocked_words' key with list of words","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py","lineNumber":795,"sourceCode":"        Load blocked words from a YAML file.\n\n        Args:\n            file_path: Path to YAML file containing blocked_words list\n\n        Expected format:\n        ```yaml\n        blocked_words:\n          - keyword: \"sensitive_term\"\n            action: \"BLOCK\"\n            description: \"Optional description\"\n        ```\n        \"\"\"\n        try:\n            with open(file_path, \"r\") as f:\n                data: Final = yaml.safe_load(f)\n\n            if not isinstance(data, dict) or \"blocked_words\" not in data:\n                raise ValueError(\"Invalid format: file must contain 'blocked_words' key with list of words\")\n\n            for word_data in data[\"blocked_words\"]:\n                if not isinstance(word_data, dict) or \"keyword\" not in word_data or \"action\" not in word_data:\n                    verbose_proxy_logger.warning(\"Skipping invalid word entry: %s\", word_data)\n                    continue\n\n                keyword = word_data[\"keyword\"].lower()\n                action = ContentFilterAction(word_data[\"action\"])\n                description = word_data.get(\"description\")\n\n                self.blocked_words[keyword] = (action, description)\n\n            verbose_proxy_logger.info(\"Loaded %s blocked words from %s\", len(data[\"blocked_words\"]), file_path)\n        except FileNotFoundError:\n            raise FileNotFoundError(f\"Blocked words file not found: {file_path}\")\n        except Exception as e:\n            raise Exception(f\"Error loading blocked words file {file_path}: {e}\")\n","sourceCodeStart":777,"sourceCodeEnd":813,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py#L777-L813","documentation":"ValueError raised while loading the blocked-words file: yaml.safe_load() succeeded but the result is either not a dict at all (empty file, or a top-level list) or a dict without the required 'blocked_words' key. The loader expects the exact schema blocked_words: [{keyword, action, description?}].","triggerScenarios":"blocked_words_file points at a YAML file whose root is a list or empty, or whose root key is named differently (words:, blocklist:, blocked:), so `isinstance(data, dict) and 'blocked_words' in data` fails.","commonSituations":"Hand-written YAML using an intuitive-but-wrong root key; passing a category/policy template file to blocked_words_file; an empty file created as a placeholder; truncation during deploy.","solutions":["Restructure the file to the expected schema with a top-level blocked_words list.","Each entry needs at least keyword and action (entries missing them are skipped with a warning, not an error).","Validate the file locally before deploy: python -c \"import yaml,sys; d=yaml.safe_load(open(sys.argv[1])); assert isinstance(d,dict) and 'blocked_words' in d\" yourfile.yaml.","Confirm you pointed blocked_words_file at a blocked-words file, not a category/policy template."],"exampleFix":"# before (wrong root key)\nwords:\n  - keyword: sensitive_term\n    action: BLOCK\n\n# after\nblocked_words:\n  - keyword: sensitive_term\n    action: BLOCK\n    description: optional note","handlingStrategy":"validation","validationCode":"# Validate the blocked-words file schema before the proxy loads it\nimport yaml\n\ndef lint_blocked_words_file(path: str) -> list[str]:\n    data = yaml.safe_load(open(path)) or {}\n    if not isinstance(data, dict) or \"blocked_words\" not in data:\n        return [f\"{path}: root must be a mapping with a 'blocked_words' list\"]\n    problems = []\n    for i, w in enumerate(data[\"blocked_words\"]):\n        if not isinstance(w, dict) or \"keyword\" not in w or \"action\" not in w:\n            problems.append(f\"{path}: entry #{i} missing keyword/action (will be skipped)\")\n    return problems","typeGuard":"from typing import Any\n\ndef is_valid_blocked_words_doc(doc: Any) -> bool:\n    \"\"\"True when the parsed YAML matches the expected {blocked_words: [...]} shape.\"\"\"\n    return (\n        isinstance(doc, dict)\n        and isinstance(doc.get(\"blocked_words\"), list)\n        and all(\n            isinstance(w, dict) and \"keyword\" in w and \"action\" in w\n            for w in doc[\"blocked_words\"]\n        )\n    )","tryCatchPattern":null,"preventionTips":["The root key is exactly 'blocked_words' — not words/blocklist/blocked.","Point blocked_words_file only at blocked-words files; category and policy templates have different schemas.","Commit a validated example file and lint all changes with the check above."],"tags":["configuration","yaml","validation","content-filter"],"backgroundTag":"schema-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}