{"record":{"id":"1c4af7bc0e15e2ab","repo":"BerriAI/litellm","slug":"pattern-is-required-for-regex-patterns","errorCode":null,"errorMessage":"pattern is required for regex patterns","messagePattern":"pattern is required for regex patterns","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py","lineNumber":744,"sourceCode":"\n    def _add_pattern(self, pattern_config: ContentFilterPattern) -> None:\n        \"\"\"\n        Add a pattern to the compiled patterns list.\n\n        Args:\n            pattern_config: ContentFilterPattern configuration\n        \"\"\"\n        try:\n            extra_config: _PatternExtraLookup = {\"keyword_pattern\": None, \"allow_word_numbers\": False}\n            if pattern_config.pattern_type == \"prebuilt\":\n                if not pattern_config.pattern_name:\n                    raise ValueError(\"pattern_name is required for prebuilt patterns\")\n                compiled = get_compiled_pattern(pattern_config.pattern_name)\n                pattern_name = pattern_config.pattern_name\n                extra_config = self._lookup_pattern_extra(pattern_name)\n            elif pattern_config.pattern_type == \"regex\":\n                if not pattern_config.pattern:\n                    raise ValueError(\"pattern is required for regex patterns\")\n                compiled = re.compile(pattern_config.pattern, re.IGNORECASE)\n                pattern_name = pattern_config.name or \"custom_regex\"\n            else:\n                raise ValueError(f\"Unknown pattern_type: {pattern_config.pattern_type}\")\n\n            keyword_pattern: Final = extra_config[\"keyword_pattern\"]\n            keyword_regex: Final = re.compile(keyword_pattern, re.IGNORECASE) if keyword_pattern else None\n\n            self.compiled_patterns.append(\n                {\n                    \"regex\": compiled,\n                    \"pattern_name\": pattern_name,\n                    \"action\": pattern_config.action,\n                    \"keyword_regex\": keyword_regex,\n                    \"allow_word_numbers\": extra_config[\"allow_word_numbers\"],\n                }\n            )\n            verbose_proxy_logger.debug(\"Added pattern: %s with action %s\", pattern_name, pattern_config.action)","sourceCodeStart":726,"sourceCodeEnd":762,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py#L726-L762","documentation":"ValueError raised in _add_pattern when a patterns[] entry declares pattern_type: \"regex\" but omits pattern. For regex patterns the `pattern` field is the regex source that gets re.compile()'d (case-insensitive); without it there is nothing to match.","triggerScenarios":"Config yaml contains litellm_params.patterns with an entry like {pattern_type: regex, action: MASK, name: my-rule} that lacks the pattern field.","commonSituations":"Example config copy-paste where the regex string was removed; author wrote the regex into `name` or `pattern_name` instead of `pattern`; YAML quoting issues silently dropped a multiline regex.","solutions":["Add the pattern field with the regex source, e.g. pattern: '\\\\d{3}-\\\\d{2}-\\\\d{4}'.","Verify the regex compiles case-insensitively (it is compiled with re.IGNORECASE) — test it locally with re.compile first.","Use name (optional) for a human-readable label; only pattern drives matching.","Quote YAML strings containing backslashes so the escape sequences survive parsing."],"exampleFix":"# before\npatterns:\n  - pattern_type: regex\n    name: internal-account\n    action: BLOCK\n\n# after\npatterns:\n  - pattern_type: regex\n    name: internal-account\n    pattern: 'ACCT-\\\\d{6}'\n    action: BLOCK","handlingStrategy":"validation","validationCode":"# Lint pattern entries: regex requires a compilable pattern\nimport re\n\ndef lint_regex_patterns(patterns: list[dict]) -> list[str]:\n    problems = []\n    for p in patterns:\n        if p.get(\"pattern_type\") == \"regex\":\n            pat = p.get(\"pattern\")\n            if not pat:\n                problems.append(f\"regex pattern missing 'pattern': {p}\")\n            else:\n                try:\n                    re.compile(pat, re.IGNORECASE)\n                except re.error as e:\n                    problems.append(f\"invalid regex {pat!r}: {e}\")\n    return problems","typeGuard":"def is_valid_regex_pattern(entry: dict) -> bool:\n    \"\"\"True when entry is a regex pattern with a non-empty, compilable source.\"\"\"\n    if entry.get(\"pattern_type\") != \"regex\":\n        return False\n    pat = entry.get(\"pattern\")\n    if not isinstance(pat, str) or not pat:\n        return False\n    import re\n    try:\n        re.compile(pat, re.IGNORECASE)\n        return True\n    except re.error:\n        return False","tryCatchPattern":null,"preventionTips":["Single-quote YAML regex strings so backslashes survive (pattern: '\\\\d{3}-\\\\d{2}-\\\\d{4}').","Test every regex with re.compile(pat, re.IGNORECASE) before it reaches the proxy.","Use name for the human label; pattern is what matches."],"tags":["configuration","validation","regex","content-filter"],"backgroundTag":"missing-config-field","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}