BerriAI/litellm · error · TypeError

'models' must be a string or list of strings

Error message

'models' must be a string or list of strings

What it means

TypeError raised by litellm.batch_completion_models when the 'models' kwarg is neither a string, list, nor tuple — e.g. a dict, set, generator, or None. After popping 'models' from kwargs the function normalizes it (str -> [str], list/tuple -> list) and rejects anything else before building the ThreadPoolExecutor.

Source

Thrown at litellm/batch_completion/main.py:237

        It sends requests concurrently and collects responses from all models that respond.
    """
    import concurrent.futures

    # ANSI escape codes for colored output

    if "model" in kwargs:
        kwargs.pop("model")
    if "models" in kwargs:
        models = kwargs.pop("models")
    else:
        raise Exception("'models' param not in kwargs")

    if isinstance(models, str):
        models = [models]
    elif isinstance(models, (list, tuple)):
        models = list(models)
    else:
        raise TypeError("'models' must be a string or list of strings")

    if len(models) == 0:
        return []

    responses: Final = []

    with concurrent.futures.ThreadPoolExecutor(max_workers=len(models)) as executor:
        futures: Final = [executor.submit(litellm.completion, *args, model=model, **kwargs) for model in models]

        for future in futures:
            try:
                result = future.result()
                if result is not None:
                    responses.append(result)
            except Exception as e:
                print_verbose(f"batch_completion_models_all_responses: model request failed: {e}")
                continue

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Convert to a list first: models=list(models) for sets/generators/ranges.
  2. Guard against None: models=models or [] (note empty list returns [] quickly).
  3. For dicts, decide whether you meant the keys: models=list(models_dict.keys()).

Example fix

# before
litellm.batch_completion_models(models={"gpt-4o", "claude-3-5-sonnet"}, messages=[...])

# after
litellm.batch_completion_models(models=list({"gpt-4o", "claude-3-5-sonnet"}), messages=[...])
Defensive patterns

Strategy: type-guard

Validate before calling

models = kwargs.get("models")
if isinstance(models, (set, tuple, range)):
    kwargs["models"] = list(models)
elif not isinstance(models, (str, list)):
    raise TypeError("models must be str or list")

Type guard

def is_valid_models_value(v) -> bool:
    return isinstance(v, (str, list, tuple))

Prevention

When it happens

Trigger: Passing models as a set, dict, range, iterator, or None: litellm.batch_completion_models(models={'gpt-4o': 1}) or models=iter([...]). A single string is fine (wrapped into a list); other iterables like generators are not.

Common situations: Using a set for dedup and passing it straight through; passing a lazily-built generator from another function; a None leaking in from an optional config field.

Related errors


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