{"record":{"id":"0580d014027ae647","repo":"BerriAI/litellm","slug":"error-watsonx-api-base-not-set-set-watsonx-api-b","errorCode":null,"errorMessage":"Error: Watsonx API base not set. Set WATSONX_API_BASE in environment variables or pass in as parameter - 'api_base='.","messagePattern":"Error: Watsonx API base not set\\. Set WATSONX_API_BASE in environment variables or pass in as parameter - 'api_base='\\.","errorType":"exception","errorClass":"WatsonXAIError","httpStatus":401,"severity":"error","filePath":"litellm/llms/watsonx/common_utils.py","lineNumber":330,"sourceCode":"\n        token: str | None = None\n\n        if wx_credentials is not None:\n            api_base = wx_credentials.get(\"url\", api_base)\n            api_key = wx_credentials.get(\"apikey\", wx_credentials.get(\"api_key\", api_key))\n            token = wx_credentials.get(\n                \"token\",\n                wx_credentials.get(\n                    \"watsonx_token\", None\n                ),  # follow format of {provider}_token, same as azure - e.g. 'azure_ad_token=..'\n            )\n        if api_key is None or not isinstance(api_key, str):\n            raise WatsonXAIError(\n                status_code=401,\n                message=\"Error: Watsonx API key not set. Set WATSONX_API_KEY in environment variables or pass in as parameter - 'api_key='.\",\n            )\n        if api_base is None or not isinstance(api_base, str):\n            raise WatsonXAIError(\n                status_code=401,\n                message=\"Error: Watsonx API base not set. Set WATSONX_API_BASE in environment variables or pass in as parameter - 'api_base='.\",\n            )\n        return WatsonXCredentials(api_key=api_key, api_base=api_base, token=cast(str | None, token))\n\n    def _prepare_payload(self, model: str, api_params: WatsonXAPIParams) -> dict:\n        payload: Final[dict] = {}\n        if model.startswith(\"deployment/\"):\n            return {}  # Deployment models do not support 'space_id' or 'project_id' in their payload\n        payload[\"model_id\"] = model\n        if api_params[\"project_id\"] is not None:\n            payload[\"project_id\"] = api_params[\"project_id\"]\n        else:\n            payload[\"space_id\"] = api_params[\"space_id\"]\n        return payload\n","sourceCodeStart":312,"sourceCodeEnd":346,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/watsonx/common_utils.py#L312-L346","documentation":"Companion check to the api_key guard in the same credential step: after merging wx_credentials, api_base must be a non-None str. The URL is resolved from the api_base argument or wx_credentials['url']; if still missing, this WatsonXAIError (401) is raised. The env-chain lookup (WATSONX_API_BASE etc.) used elsewhere does not run on this branch, so the value must arrive via argument or the credentials dict.","triggerScenarios":"Passing wx_credentials with only 'apikey' and no 'url'; calling with api_base=None and no url in credentials; api_base accidentally set to a non-string (e.g. a URL object or None from a failed os.getenv chain).","commonSituations":"Config templating that leaves the url field blank in non-prod; splitting credentials across multiple dicts and dropping the url one; assuming WATSONX_API_BASE env is read here when only argument/credentials are checked.","solutions":["Pass api_base explicitly to the call.","Or include \"url\" in wx_credentials: wx_credentials={'url': 'https://us-south.ml.cloud.ibm.com', 'apikey': ...}.","Also export WATSONX_API_BASE so other watsonx paths resolve it consistently.","Validate the assembled config object before the request: url must be a non-empty string."],"exampleFix":"# before\nwx_credentials = {\"apikey\": os.environ[\"WATSONX_APIKEY\"]}  # no url\n# ... -> WatsonXAIError: Watsonx API base not set...\n\n# after\nwx_credentials = {\n    \"url\": \"https://us-south.ml.cloud.ibm.com\",\n    \"apikey\": os.environ[\"WATSONX_APIKEY\"],\n}","handlingStrategy":"validation","validationCode":"def watsonx_url_ok(api_base: str | None, wx_credentials: dict | None) -> bool:\n    if isinstance(api_base, str) and api_base:\n        return True\n    if isinstance(wx_credentials, dict):\n        url = wx_credentials.get(\"url\")\n        if isinstance(url, str) and url:\n            return True\n    return False\n\nif not watsonx_url_ok(api_base, wx_credentials):\n    raise RuntimeError(\"WatsonX needs api_base or wx_credentials['url']\")","typeGuard":"const watsonxUrlOk = (apiBase?: string, wx?: Record<string, unknown>): boolean =>\n  (typeof apiBase === \"string\" && apiBase.length > 0) ||\n  (typeof wx?.url === \"string\" && (wx.url as string).length > 0);","tryCatchPattern":"from litellm.llms.watsonx.common_utils import WatsonXAIError\n\ntry:\n    resp = litellm.completion(model=\"watsonx/...\", messages=msgs)\nexcept WatsonXAIError as e:\n    if \"Watsonx API base not set\" in e.message:\n        raise RuntimeError(\"Pass api_base or wx_credentials={'url': ...}\") from e\n    raise","preventionTips":["Always include 'url' in wx_credentials alongside 'apikey'.","Also export WATSONX_API_BASE for the other watsonx code paths.","Validate the whole credentials object (url + apikey) once at startup."],"tags":["watsonx","api-base","url-configuration","credentials","litellm"],"backgroundTag":"missing-api-base-url","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}