{"record":{"id":"b1623ea81261152f","repo":"BerriAI/litellm","slug":"custom-llm-provider-is-required-b1623e","errorCode":null,"errorMessage":"custom_llm_provider is required","messagePattern":"custom_llm_provider is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/endpoints/speech/speech_to_completion_bridge/handler.py","lineNumber":41,"sourceCode":"\n\nclass SpeechToCompletionBridgeHandler:\n    def __init__(self):\n        from .transformation import SpeechToCompletionBridgeTransformationHandler\n\n        super().__init__()\n        self.transformation_handler = SpeechToCompletionBridgeTransformationHandler()\n\n    def validate_input_kwargs(self, kwargs: dict) -> SpeechToCompletionBridgeHandlerInputKwargs:\n        from litellm import LiteLLMLoggingObj\n\n        model: Final = kwargs.get(\"model\")\n        if model is None or not isinstance(model, str):\n            raise ValueError(\"model is required\")\n\n        custom_llm_provider: Final = kwargs.get(\"custom_llm_provider\")\n        if custom_llm_provider is None or not isinstance(custom_llm_provider, str):\n            raise ValueError(\"custom_llm_provider is required\")\n\n        input: Final = kwargs.get(\"input\")\n        if input is None or not isinstance(input, str):\n            raise ValueError(\"input is required\")\n\n        optional_params: Final = kwargs.get(\"optional_params\")\n        if optional_params is None or not isinstance(optional_params, dict):\n            raise ValueError(\"optional_params is required\")\n\n        litellm_params: Final = kwargs.get(\"litellm_params\")\n        if litellm_params is None or not isinstance(litellm_params, dict):\n            raise ValueError(\"litellm_params is required\")\n\n        headers = kwargs.get(\"headers\")\n        if headers is None or not isinstance(headers, dict):\n            raise ValueError(\"headers is required\")\n\n        headers = kwargs.get(\"headers\")","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/endpoints/speech/speech_to_completion_bridge/handler.py#L23-L59","documentation":"Thrown by SpeechToCompletionBridgeHandler.validate_input_kwargs when 'custom_llm_provider' is absent from kwargs or is not a str (handler.py:41). The bridge needs the resolved provider name (e.g. 'openai') to transform the TTS request into a chat/completions call. Because litellm's main_router normally injects custom_llm_provider, hitting this means the call bypassed standard routing or the provider could not be resolved.","triggerScenarios":"Calling speech_to_completion_bridge_handler.speech(...) directly without custom_llm_provider; a model string formatted so get_llm_provider cannot resolve a provider (e.g. an unknown prefix) causing custom_llm_provider to be dropped; patched/mocked routing code that strips the key from kwargs.","commonSituations":"Custom integrations that call the bridge handler instead of litellm.audio_speech; model names without a recognizable provider prefix ('openai/...', 'azure/...'); unit tests with mocked router outputs.","solutions":["Use litellm.audio_speech()/litellm.speech() rather than invoking the bridge handler directly, so routing populates custom_llm_provider","If direct invocation is required, pass custom_llm_provider=\"openai\" explicitly","Prefix the model with the provider (e.g. 'openai/gpt-4o-audio-preview') so the provider is resolvable"],"exampleFix":"# before\nspeech_to_completion_bridge_handler.speech(model=\"gpt-4o-audio-preview\", input=\"hi\", voice=\"alloy\", optional_params={}, litellm_params={}, headers={}, logging_obj=logging_obj)\n\n# after\nspeech_to_completion_bridge_handler.speech(model=\"gpt-4o-audio-preview\", input=\"hi\", voice=\"alloy\", optional_params={}, litellm_params={}, headers={}, logging_obj=logging_obj, custom_llm_provider=\"openai\")","handlingStrategy":"validation","validationCode":"provider = kwargs.get(\"custom_llm_provider\")\nif not isinstance(provider, str):\n    # let litellm routing resolve it instead of calling the handler directly\n    kwargs[\"model\"] = f\"openai/{model}\"  # explicit provider prefix\n    # or: kwargs[\"custom_llm_provider\"] = \"openai\"","typeGuard":"def has_resolved_provider(kwargs: dict) -> bool:\n    return isinstance(kwargs.get(\"custom_llm_provider\"), str)","tryCatchPattern":"try:\n    resp = litellm.audio_speech(model=\"openai/gpt-4o-audio-preview\", input=text, voice=voice)\nexcept ValueError as e:\n    if \"custom_llm_provider is required\" in str(e):\n        logger.error(\"Provider unresolved; prefix model with provider name\")\n        raise","preventionTips":["Prefer litellm.audio_speech() over direct handler invocation so routing resolves the provider","Prefix model names with an explicit provider ('openai/...', 'azure/...')","Never strip custom_llm_provider from kwargs in middleware or mocks"],"tags":["validation","tts","provider","routing"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}