{"record":{"id":"70d5eeb3903d91ce","repo":"BerriAI/litellm","slug":"cisco-ai-defense-guardrail-invalid-rule-definitio","errorCode":null,"errorMessage":"Cisco AI Defense guardrail: invalid rule definition: {rule!r}","messagePattern":"Cisco AI Defense guardrail: invalid rule definition: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py","lineNumber":940,"sourceCode":"                    rule = dumped\n\n        if isinstance(rule, dict):\n            normalized: Final[dict[str, object]] = {}\n            rule_name: Final = rule.get(\"rule_name\")\n            if rule_name:\n                normalized[\"rule_name\"] = rule_name\n            entity_types: Final = rule.get(\"entity_types\")\n            if entity_types:\n                normalized[\"entity_types\"] = list(entity_types)\n            rule_id: Final = rule.get(\"rule_id\")\n            if rule_id is not None:\n                normalized[\"rule_id\"] = rule_id\n            classification: Final = rule.get(\"classification\")\n            if classification:\n                normalized[\"classification\"] = classification\n            return normalized\n\n        raise ValueError(f\"Cisco AI Defense guardrail: invalid rule definition: {rule!r}\")\n\n    # ------------------------------------------------------------------\n    # Response processing\n    # ------------------------------------------------------------------\n\n    def _finalize_inspection(\n        self,\n        inspect_response: dict[str, Any],\n        request_data: dict,\n        context: _ScanContext,\n        start_time: datetime,\n        response_obj: object = None,\n    ) -> dict[str, object]:\n        \"\"\"Parse, log, and (optionally) raise/redact on the Cisco verdict.\n\n        ``context.direction`` is ``\"input\"`` for request scans and ``\"output\"``\n        for response scans (used for metadata namespacing and response headers).\n        ``response_obj`` is the LiteLLM response object (or MCP tool-call","sourceCodeStart":922,"sourceCodeEnd":958,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/cisco_ai_defense/cisco_ai_defense.py#L922-L958","documentation":"ValueError raised while normalizing the Cisco AI Defense guardrail's rules config (_normalize_rules-style path): a rules entry was neither a plain string (shorthand for a rule name) nor a mapping the normalizer could extract a rule_name from. The offending value is echoed with {rule!r}, so the config mistake is directly visible. This is a config-shape validation error raised at guardrail init time.","triggerScenarios":"Passing rules: [123, null, [], {\"name\": ...}] — i.e., entries that are not strings and not dicts carrying the recognized keys (rule_name via shorthand, entity_types, rule_id, classification). Also strings that aren't matched and dicts missing every recognized field fall through to this raise.","commonSituations":"Converting a rules config from another format (YAML anchors producing None, JSON numbers), typos like {\"rulename\": \"x\"} instead of rule_name, or copy-pasting Cisco console JSON where rule entries are nested one level deeper than expected.","solutions":["Check the echoed {rule!r} in the error — it shows exactly which entry is invalid.","Make each rules entry either a plain string (\"rule-name\") or a dict with recognized keys: rule_name (or shorthand), entity_types (list), rule_id, classification.","Validate the config with a YAML/JSON schema check before deploying; reload the proxy after fixing."],"exampleFix":"# before — entry has unrecognized keys only\nlitellm_params:\n  guardrail: cisco_ai_defense\n  rules:\n    - rulename: prompt_injection\n\n# after\nlitellm_params:\n  guardrail: cisco_ai_defense\n  rules:\n    - rule_name: prompt_injection\n      entity_types: [PROMPT_INJECTION]","handlingStrategy":"type-guard","validationCode":"def normalize_rule(r):\n    if isinstance(r, str) and r.strip():\n        return {\"rule_name\": r}\n    if isinstance(r, dict) and (r.get(\"rule_name\") or any(k in r for k in (\"entity_types\", \"rule_id\", \"classification\"))):\n        out = {k: v for k, v in r.items() if k in (\"rule_name\", \"entity_types\", \"rule_id\", \"classification\")}\n        if \"rule_name\" not in out:\n            out[\"rule_name\"] = r.get(\"rule_name\") or r.get(\"name\")\n        return out\n    raise ValueError(f\"invalid cisco rule definition: {r!r}\")\nrules = [normalize_rule(r) for r in config_rules]  # run before writing config","typeGuard":"from typing import Any\ndef is_valid_cisco_rule(rule: Any) -> bool:\n    if isinstance(rule, str):\n        return bool(rule.strip())\n    return (isinstance(rule, dict)\n            and isinstance(rule.get(\"rule_name\"), str)\n            and (\"entity_types\" not in rule or isinstance(rule[\"entity_types\"], list)))","tryCatchPattern":null,"preventionTips":["Validate the rules block in CI with the same shape rules the guardrail enforces (string OR dict with rule_name/entity_types/rule_id/classification).","Generate config from typed data (pydantic model) rather than hand-written YAML for rule lists.","Unit-test guardrail config against a schema test that mirrors _normalize_rules behavior."],"tags":["litellm","guardrails","cisco-ai-defense","configuration","validation","rules"],"backgroundTag":"config-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}