BerriAI/litellm · error · Exception

Max langfuse clients reached: {litellm.initialized_langfuse_

Error message

Max langfuse clients reached: {litellm.initialized_langfuse_clients} is greater than {MAX_LANGFUSE_INITIALIZED_CLIENTS}

What it means

LiteLLM refuses to create more than MAX_LANGFUSE_INITIALIZED_CLIENTS Langfuse clients. Each Langfuse client spawns background threads, and litellm once hit 100% CPU from repeated client creation, so safe_init_langfuse_client enforces a hard cap. The counter is litellm.initialized_langfuse_clients, incremented on every successful init and never decremented in-process.

Source

Thrown at litellm/integrations/langfuse/langfuse.py:216

                host=self.upstream_langfuse_host,
                release=self.upstream_langfuse_release,
                debug=(upstream_langfuse_debug if upstream_langfuse_debug is not None else False),
            )
        else:
            self.upstream_langfuse = None

    def safe_init_langfuse_client(self, parameters: dict) -> Langfuse:
        """
        Safely init a langfuse client if the number of initialized clients is less than the max

        Note:
            - Langfuse initializes 1 thread everytime a client is initialized.
            - We've had an incident in the past where we reached 100% cpu utilization because Langfuse was initialized several times.
        """
        from langfuse import Langfuse

        if litellm.initialized_langfuse_clients >= MAX_LANGFUSE_INITIALIZED_CLIENTS:
            raise Exception(
                f"Max langfuse clients reached: {litellm.initialized_langfuse_clients} is greater than {MAX_LANGFUSE_INITIALIZED_CLIENTS}"
            )
        langfuse_client: Final = Langfuse(**parameters)
        litellm.initialized_langfuse_clients += 1
        verbose_logger.debug("Created langfuse client number %s", litellm.initialized_langfuse_clients)
        return langfuse_client

    @staticmethod
    def add_metadata_from_header(litellm_params: dict, metadata: dict) -> dict:
        """
        Adds metadata from proxy request headers to Langfuse logging if keys start with "langfuse_"
        and overwrites litellm_params.metadata if already included.

        For example if you want to append your trace to an existing `trace_id` via header, send
        `headers: { ..., langfuse_existing_trace_id: your-existing-trace-id }` via proxy request.
        """
        if litellm_params is None:
            return metadata

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Consolidate to a single set of Langfuse credentials (set LANGFUSE_PUBLIC_KEY/LANGFUSE_SECRET_KEY globally instead of per-request dynamic params)
  2. Remove per-request langfuse_* metadata/header overrides in the proxy so the shared client is reused
  3. Restart the proxy process to reset litellm.initialized_langfuse_clients if it grew from historical churn
  4. If distinct tenants are genuinely required, route different tenants to separate proxy deployments rather than one process

Example fix

# before (new client per request)
metadata = {"langfuse_public_key": tenant_pk, "langfuse_secret": tenant_sk}
response = litellm.completion(..., metadata=metadata)

# after (shared global client)
import os
os.environ["LANGFUSE_PUBLIC_KEY"] = shared_pk
os.environ["LANGFUSE_SECRET_KEY"] = shared_sk
response = litellm.completion(...)  # reuses the single initialized client
Defensive patterns

Strategy: validation

Validate before calling

import litellm

MAX_CLIENTS = 50  # keep in sync with litellm's MAX_LANGFUSE_INITIALIZED_CLIENTS

def can_init_langfuse() -> bool:
    return litellm.initialized_langfuse_clients < MAX_CLIENTS

Prevention

When it happens

Trigger: Passing different langfuse_public_key/langfuse_secret/langfuse_host values per request via dynamic callback params (proxy headers such as x-litellm-langfuse-public-key), which forces a new client each time; repeatedly constructing LangfuseLogger instances in a long-lived process; many virtual keys each with their own langfuse credentials.

Common situations: Multi-tenant proxy setups where each team injects its own Langfuse credentials through request metadata/header overrides. After the cap of distinct credential sets is reached, every further request with new credentials raises this from the logging path.

Related errors


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