BerriAI/litellm · error · ValueError
classifier_llm_config.classification_rubric and system_promp
Error message
classifier_llm_config.classification_rubric and system_prompt are mutually exclusive: system_prompt replaces the built-in rubric the preset would select. Drop one.
What it means
Config validator on ComplexityRouterConfig: both a classification_rubric preset and a custom system_prompt are set. The system_prompt silently replaces the rubric the preset would choose, so the ambiguous configuration is rejected — keep one.
Source
Thrown at litellm/router_strategy/complexity_router/config.py:338
@field_validator("system_prompt")
@classmethod
def _reject_blank_system_prompt(cls, value: str | None) -> str | None:
# A blank string is a misconfiguration, not a request for the default: it would send an
# empty system role and leave the classifier with no rubric at all. None means default.
if value is not None and not value.strip():
raise ValueError("classifier_llm_config.system_prompt must be non-empty; omit it to use the default rubric")
return value
@model_validator(mode="after")
def _reject_rubric_with_system_prompt(self) -> "ClassifierLLMConfig":
# A custom prompt is the classifier's whole system role, so a preset set alongside it would never
# reach the wire. Rejecting it beats honoring one of two settings the operator asked for.
#
# None, not model_fields_set, is what marks the preset unchosen: this model is dumped and
# re-validated in place (see /auto_router/test_routing), and a dump re-states every field, so
# keying on fields_set would reject on the second pass what it accepted on the first.
if self.system_prompt is not None and self.classification_rubric is not None:
raise ValueError(
"classifier_llm_config.classification_rubric and system_prompt are mutually exclusive: system_prompt replaces "
"the built-in rubric the preset would select. Drop one."
)
return self
class ComplexityRouterConfig(BaseModel):
"""Configuration for the ComplexityRouter."""
# string = pin; list = random pick when adaptive=False, soft-floor home pool when adaptive=True
tiers: dict[str, str | list[str]] = Field(
default_factory=lambda: DEFAULT_TIER_MODELS.copy(),
description=(
"Mapping of complexity tiers to a model or model pool. "
"A list is randomly picked from when adaptive=False, and used as a soft-floor home pool when adaptive=True"
),
)
View on GitHub (pinned to 77b7c6c40c)
Solutions
- Drop either classification_rubric or system_prompt from classifier_llm_config; they are mutually exclusive.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at litellm/router_strategy/complexity_router/config.py:338 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/1dafdb48140c6ad0.
Report an issue: GitHub.