BerriAI/litellm · error · Exception

dynamic_api_key needs to be a string. dynamic_api_key={dynam

Error message

dynamic_api_key needs to be a string. dynamic_api_key={dynamic_api_key}

What it means

dynamic_api_key string check on the OpenAI-compatible-endpoint path: after resolving a known endpoint (perplexity/deepinfra/wandb/meta/...), the dynamic_api_key resolved from arguments or env is not a string.

Source

Thrown at litellm/litellm_core_utils/get_llm_provider_logic.py:356

                        custom_llm_provider = "hyperbolic"
                        dynamic_api_key = get_secret_str("HYPERBOLIC_API_KEY")
                    elif endpoint == "https://ai-gateway.vercel.sh/v1":
                        custom_llm_provider = "vercel_ai_gateway"
                        dynamic_api_key = get_secret_str("VERCEL_AI_GATEWAY_API_KEY")
                    elif endpoint == "https://api.inference.wandb.ai/v1":
                        custom_llm_provider = "wandb"
                        dynamic_api_key = get_secret_str("WANDB_API_KEY")
                    elif endpoint == "https://pinstripes.io/v1":
                        custom_llm_provider = "pinstripes"
                        dynamic_api_key = get_secret_str("PINSTRIPES_API_KEY")
                    elif endpoint == "https://api.meta.ai/v1":
                        custom_llm_provider = "meta"
                        dynamic_api_key = get_secret_str("META_API_KEY")

                    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. dynamic_api_key={dynamic_api_key}")
                    return model, custom_llm_provider, dynamic_api_key, api_base

        # check if model in known model provider list  -> for huggingface models, raise exception as they don't have a fixed provider (can be togetherai, anyscale, baseten, runpod, et.)
        ## openai - chatcompletion + text completion
        if (
            model in litellm.open_ai_chat_completion_models
            or "ft:gpt-3.5-turbo" in model
            or "ft:gpt-4" in model  # catches ft:gpt-4-0613, ft:gpt-4o
            or model in litellm.openai_image_generation_models
            or model.startswith("gpt-image")
            or model in litellm.openai_video_generation_models
        ):
            custom_llm_provider = "openai"
        elif model in litellm.open_ai_text_completion_models:
            custom_llm_provider = "text-completion-openai"
        ## anthropic
        elif model in litellm.anthropic_models:
            if litellm.AnthropicTextConfig._is_anthropic_text_model(model):

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Ensure the api_key argument is the string value of the credential.
  2. Decode bytes keys: api_key.decode() where applicable.
  3. Unwrap SecretStr via get_secret_value().

Example fix

# before
litellm.completion(model='openai/...', api_base='https://api.deepinfra.com/v1/openai', api_key=b'sk-...')

# after
litellm.completion(model='openai/...', api_base='https://api.deepinfra.com/v1/openai', api_key='sk-...')
Defensive patterns

Strategy: validation

Validate before calling

def resolved_key_is_str(key) -> bool:
    return key is None or isinstance(key, str)

Prevention

When it happens

Trigger: Provider endpoint matched but a non-string api_key object was supplied, or a get_secret_str-style lookup returned a non-string object injected by custom secret tooling.

Common situations: Secret managers returning objects/bytes, Pydantic SecretStr, or config loading code that stores credentials as dicts.

Related errors


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