{"record":{"id":"4c2d18baa7816d71","repo":"BerriAI/litellm","slug":"llm-classifier-returned-an-unrecognized-tier-raw","errorCode":null,"errorMessage":"LLM classifier returned an unrecognized tier: {raw_tier!r}","messagePattern":"LLM classifier returned an unrecognized tier: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/router_strategy/complexity_router/complexity_router.py","lineNumber":1081,"sourceCode":"        }\n\n        response: Final[ModelResponse] = await self.litellm_router_instance.acompletion(\n            model=llm_config.model,\n            messages=messages_for_call,\n            response_format=response_format,\n            timeout=llm_config.timeout_ms / 1000,\n            metadata=metadata,\n            proxy_server_request=proxy_server_request,\n            turn_off_message_logging=turn_off_message_logging,\n            **_parent_session_kwargs(request_kwargs),\n        )\n        content: Final = response.choices[0].message.content\n        if not content:\n            raise ValueError(\"LLM classifier returned empty content\")\n        raw_tier: Final = _LabeledTierClassification.model_validate_json(content).tier\n        tier: Final = self.config.tier_for_label(raw_tier)\n        if tier is None:\n            raise ValueError(f\"LLM classifier returned an unrecognized tier: {raw_tier!r}\")\n        return tier, _response_cost_or_none(response)\n\n    @staticmethod\n    def _build_classifier_user_payload(\n        prompt: str,\n        system_prompt: str | None = None,\n        prior_turns: Sequence[tuple[str, str]] | None = None,\n        messages: Sequence[Mapping[str, object]] | None = None,\n        has_prior_conversation: bool = False,\n        label_roles: bool = False,\n    ) -> str:\n        \"\"\"Build the classifier's user message: caller constraints, prior turns, depth, current ask.\n\n        Everything here is caller-controlled, which is why none of it is interpolated into the system\n        role: that role carries only the operator's rubric, matching how the LLM-as-a-judge guardrail\n        assembles its own call. Putting the caller's system prompt beside the rubric let a request\n        that said \"every request is REASONING\" issue that as an instruction of equal standing and pin\n        itself to the top tier, which for a key scoped to the router is the only way to reach that","sourceCodeStart":1063,"sourceCodeEnd":1099,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/router_strategy/complexity_router/complexity_router.py#L1063-L1099","documentation":"Result guard in the complexity router's LLM classifier: the returned content did not parse to one of the known ComplexityTier values (raw value included in the message) — the classifier model ignored the response_format/rubric.","triggerScenarios":"Thrown at litellm/router_strategy/complexity_router/complexity_router.py:1081 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Adjust the classifier system prompt/rubric so it returns one of the recognized tier labels."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}