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

  1. Pass api_base as a plain string: api_base='http://host:8000/v1'.
  2. If config is structured, extract the string field first (config['api_base']['url']).
  3. 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

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


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/d5f945e0dcdfe485. Report an issue: GitHub.