BerriAI/litellm · error · ValueError

input must be a string or a list

Error message

input must be a string or a list

What it means

ValueError from CachingHandler.handle_kwargs_input_list_or_str: kwargs['input'] is neither a str nor a list. LiteLLM's embedding cache layer normalizes input by wrapping a string into a one-element list and passing lists through; any other type (None, int, dict) fails the isinstance chain and is rejected.

Source

Thrown at litellm/caching/caching_handler.py:378

                        self.preset_cache_key
                        or self.request_kwargs.get("cache_key")
                        or litellm.cache.get_cache_key(**self.request_kwargs)
                    )
                    if hasattr(cached_result, "_hidden_params"):
                        cached_result._hidden_params["cache_key"] = cache_key
                    return CachingHandlerResponse(cached_result=cached_result)
        return CachingHandlerResponse(cached_result=cached_result)

    def handle_kwargs_input_list_or_str(self, kwargs: dict[str, Any]) -> list[str]:
        """
        Handles the input of kwargs['input'] being a list or a string
        """
        if isinstance(kwargs["input"], str):
            return [kwargs["input"]]
        elif isinstance(kwargs["input"], list):
            return kwargs["input"]
        else:
            raise ValueError("input must be a string or a list")

    def _extract_model_from_cached_results(self, non_null_list: list[tuple[int, CachedEmbedding]]) -> str | None:
        """
        Helper method to extract the model name from cached results.

        Args:
            non_null_list: List of (idx, cr) tuples where cr is the cached result dict

        Returns:
            Optional[str]: The model name if found, None otherwise
        """
        for _, cr in non_null_list:
            if isinstance(cr, dict) and cr.get("model"):
                return cr["model"]
        return None

    def _process_async_embedding_cached_response(
        self,

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Convert before calling: wrap strings as-is, convert arrays with input=list(arr), and ensure input is never None.
  2. For token ids, pass a list of ints (list[list[int]] for multiple inputs).
  3. Add a type check at your call site for data sourced from users/pipelines.

Example fix

# before
litellm.embedding(model="text-embedding-3-small", input=np.array([1,2,3]))

# after
litellm.embedding(model="text-embedding-3-small", input=list(np.array([1,2,3])))
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(user_input, (str, list)):
    user_input = list(user_input) if hasattr(user_input, "__iter__") else [user_input]

Type guard

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

Prevention

When it happens

Trigger: Calling litellm.embedding (or aembedding) with caching enabled where input is not a string or list — e.g. input=None, input=12345 (token ids as a plain int, not wrapped in a list), or a numpy array/tensor passed directly.

Common situations: Passing token-id arrays (list-of-ints is fine, but a bare numpy array/torch tensor is not); a None default leaking from upstream config; passing bytes or other exotic payload types.

Related errors


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