BerriAI/litellm · error · Exception
api base needs to be a string. api_base={api_base}
Error message
api base needs to be a string. api_base={api_base} What it means
Type guard in get_llm_provider for the 'provider/model' path: the model prefix matched a known provider, but the supplied api_base is not a string (e.g. a dict, list, or number). The bare Exception is re-raised as BadRequestError ('GetLLMProvider Exception - api base needs to be a string...') by the enclosing handler.
Source
Thrown at litellm/litellm_core_utils/get_llm_provider_logic.py:225
if (
model.split("/", 1)[0] in litellm.provider_list
and model.split("/", 1)[0] not in litellm.model_list_set
and len(model.split("/"))
> 1 # handle edge case where user passes in `litellm --model mistral` https://github.com/BerriAI/litellm/issues/1351
):
return _get_openai_compatible_provider_info(
model=model,
api_base=api_base,
api_key=api_key,
dynamic_api_key=dynamic_api_key,
litellm_params=litellm_params,
)
elif model.split("/", 1)[0] in litellm.provider_list:
custom_llm_provider = model.split("/", 1)[0]
model = model.split("/", 1)[1]
if api_base is not None and not isinstance(api_base, str):
raise Exception(f"api base needs to be a string. api_base={api_base}")
if dynamic_api_key is not None and not isinstance(dynamic_api_key, str):
raise Exception(f"dynamic_api_key needs to be a string. Got type={type(dynamic_api_key).__name__}")
return model, custom_llm_provider, dynamic_api_key, api_base
# check if api base is a known openai compatible endpoint
if api_base:
for endpoint in litellm.openai_compatible_endpoints:
if _endpoint_matches_api_base(endpoint, api_base):
if endpoint == "api.perplexity.ai":
custom_llm_provider = "perplexity"
dynamic_api_key = get_secret_str("PERPLEXITYAI_API_KEY")
elif endpoint == "api.endpoints.anyscale.com/v1":
custom_llm_provider = "anyscale"
dynamic_api_key = get_secret_str("ANYSCALE_API_KEY")
elif endpoint == "api.deepinfra.com/v1/openai":
custom_llm_provider = "deepinfra"
dynamic_api_key = get_secret_str("DEEPINFRA_API_KEY")
elif endpoint == "api.mistral.ai/v1":
custom_llm_provider = "mistral"View on GitHub (pinned to 6c2dcb801b)
Solutions
- Pass api_base as a plain string: api_base='http://host:8000/v1'.
- If config is structured, extract the string field first (config['api_base']['url']).
- Validate config at load time with a schema that types api_base as string.
Example fix
# before
litellm.completion(model='openai/m', api_base={'url': 'http://x:8000/v1'}, ...)
# after
litellm.completion(model='openai/m', api_base='http://x:8000/v1', ...) Defensive patterns
Strategy: validation
Validate before calling
def api_base_valid(api_base) -> bool:
return api_base is None or isinstance(api_base, str) Type guard
function isApiBase(v: unknown): v is string | undefined {
return v === undefined || v === null || typeof v === 'string';
} Prevention
- Type api_base as Optional[str] in config models so non-strings fail at load time.
- Extract the URL string from structured config before building call kwargs.
- Add startup config validation for provider settings.
When it happens
Trigger: litellm.completion(model='openai/llama3', api_base={'url': ...}) or any provider-prefixed model where api_base comes from config as a non-string type (dict from YAML, None-adjacent objects, Pydantic objects).
Common situations: Config files storing api_base as a structured object, programmatic config where the wrong key is passed, or deserialized JSON config with nested objects.
Related errors
- limit must be an integer
- Event hook {hook} is not in the supported event hooks {suppo
- Event hook {event_hook} is not in the supported event hooks
- Invalid environment: {environment}. Please use one of the fo
- bucket_name must be provided for S3 destination
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/d5f945e0dcdfe485.
Report an issue: GitHub.