{"record":{"id":"fe7737f652b57489","repo":"BerriAI/litellm","slug":"custom-llm-provider-capitalize-exception-use","errorCode":null,"errorMessage":"{custom_llm_provider.capitalize()}Exception - Use 'watsonx_text' route instead. IBM WatsonX does not support `/text/chat` endpoint. - {error_str}","messagePattern":"(.+?)Exception - Use 'watsonx_text' route instead\\. IBM WatsonX does not support `/text/chat` endpoint\\. - (.+?)","errorType":"exception","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"litellm/litellm_core_utils/exception_mapping_utils.py","lineNumber":746,"sourceCode":"            llm_provider=custom_llm_provider,\n            response=getattr(original_exception, \"response\", None),\n            litellm_debug_info=extra_information,\n        )\n    elif \"token_quota_reached\" in error_str:\n        raise RateLimitError(\n            message=f\"{custom_llm_provider.capitalize()}Exception: Rate Limit Errror - {error_str}\",\n            llm_provider=custom_llm_provider,\n            model=model,\n            response=getattr(original_exception, \"response\", None),\n        )\n    elif \"The server received an invalid response from an upstream server.\" in error_str:\n        raise litellm.InternalServerError(\n            message=f\"{custom_llm_provider.capitalize()}Exception - {original_exception.message}\",\n            llm_provider=custom_llm_provider,\n            model=model,\n        )\n    elif \"model_no_support_for_function\" in error_str:\n        raise BadRequestError(\n            message=f\"{custom_llm_provider.capitalize()}Exception - Use 'watsonx_text' route instead. IBM WatsonX does not support `/text/chat` endpoint. - {error_str}\",\n            llm_provider=custom_llm_provider,\n            model=model,\n        )\n    elif hasattr(original_exception, \"status_code\"):\n        if original_exception.status_code == 500:\n            raise litellm.InternalServerError(\n                message=f\"{custom_llm_provider.capitalize()}Exception - {original_exception.message}\",\n                llm_provider=custom_llm_provider,\n                model=model,\n            )\n        elif original_exception.status_code == 401 or original_exception.status_code == 403:\n            raise AuthenticationError(\n                message=f\"{custom_llm_provider.capitalize()}Exception - {original_exception.message}\",\n                llm_provider=custom_llm_provider,\n                model=model,\n            )\n        elif original_exception.status_code == 400:","sourceCodeStart":728,"sourceCodeEnd":764,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/litellm_core_utils/exception_mapping_utils.py#L728-L764","documentation":"When an OpenAI-like provider error contains 'model_no_support_for_function' - IBM watsonx rejecting a tools/functions request on the /text/chat path - litellm raises BadRequestError with explicit guidance: use the 'watsonx_text' route instead, because IBM WatsonX does not support the /text/chat endpoint for that call. This is litellm routing you to a code path that can carry the request, not a transient failure.","triggerScenarios":"Calling litellm.completion with tools=/functions=/tool_choice= against a watsonx model through a route whose endpoint refuses function payloads; mixing OpenAI-style tool schemas into a watsonx chat-route call.","commonSituations":"Porting an OpenAI tool-calling app to watsonx unchanged; enabling tools on a granite model via the chat route; provider-side route changes after a litellm upgrade.","solutions":["Switch the model prefix to the watsonx_text route (model=\"watsonx_text/<model_id>\") so the request hits the supported endpoint","If tools are optional, drop tools=/functions= and parse structured output from plain text instead","Use a watsonx model/version with native function-calling support","After a litellm upgrade, re-check the watsonx routing docs for current route names"],"exampleFix":"# before - tools through a route that rejects them\nresp = litellm.completion(\n    model=\"watsonx/<model_id>\",\n    messages=msgs,\n    tools=[{\"type\": \"function\", \"function\": {\"name\": \"get_weather\", \"parameters\": {...}}}],\n)\n\n# after - same model via the supported route\nresp = litellm.completion(\n    model=\"watsonx_text/<model_id>\",\n    messages=msgs,\n    tools=[{\"type\": \"function\", \"function\": {\"name\": \"get_weather\", \"parameters\": {...}}}],\n)","handlingStrategy":"fallback","validationCode":"def resolve_watsonx_model(model: str, tools: list | None) -> str:\n    \"\"\"Send tool-calling traffic down the supported watsonx route.\"\"\"\n    if tools and model.startswith(\"watsonx/\") and not model.startswith(\"watsonx_text/\"):\n        return model.replace(\"watsonx/\", \"watsonx_text/\", 1)\n    return model","typeGuard":"import litellm\n\ndef is_watsonx_route_error(exc: BaseException) -> bool:\n    return (\n        isinstance(exc, litellm.BadRequestError)\n        and \"watsonx_text\" in getattr(exc, \"message\", \"\")\n    )","tryCatchPattern":"try:\n    resp = litellm.completion(model=model, messages=messages, tools=tools)\nexcept litellm.BadRequestError as e:\n    if \"watsonx_text\" in e.message and model.startswith(\"watsonx/\"):\n        # retry once on the supported route\n        resp = litellm.completion(\n            model=model.replace(\"watsonx/\", \"watsonx_text/\", 1),\n            messages=messages,\n            tools=tools,\n        )\n    else:\n        raise","preventionTips":["Centralize model-string selection so route prefixes are correct everywhere","Integration-test tool-calling once per provider at onboarding","Watch litellm release notes for watsonx route changes"],"tags":["litellm","watsonx","function-calling","tools","bad-request","route"],"backgroundTag":"unsupported-endpoint","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}