BerriAI/litellm · error · Exception

External Customer ID is not set. Charge_by={charge_by}. User

Error message

External Customer ID is not set. Charge_by={charge_by}. User_id={user_id}. End_user_id={end_user_id}. Team_id={team_id}

What it means

Exception raised by the Lago integration when the identity field selected by LAGO_API_CHARGE_BY is None on the request, so there is no external_customer_id to attach the billing event to. The message dumps all three candidate ids for debugging.

Source

Thrown at litellm/integrations/lago.py:107

        if os.getenv("LAGO_API_CHARGE_BY", None) is not None and isinstance(os.environ["LAGO_API_CHARGE_BY"], str):
            if os.environ["LAGO_API_CHARGE_BY"] in [
                "end_user_id",
                "user_id",
                "team_id",
            ]:
                charge_by = os.environ["LAGO_API_CHARGE_BY"]
            else:
                raise Exception("invalid LAGO_API_CHARGE_BY set")

        if charge_by == "end_user_id":
            external_customer_id = end_user_id
        elif charge_by == "team_id":
            external_customer_id = team_id
        elif charge_by == "user_id":
            external_customer_id = user_id

        if external_customer_id is None:
            raise Exception(
                f"External Customer ID is not set. Charge_by={charge_by}. User_id={user_id}. End_user_id={end_user_id}. Team_id={team_id}"
            )

        returned_val: Final = {
            "event": {
                "transaction_id": str(uuid.uuid4()),
                "external_subscription_id": external_customer_id,
                "code": os.getenv("LAGO_API_EVENT_CODE"),
                "properties": {"model": model, "response_cost": cost, **usage},
            }
        }

        verbose_logger.debug("\x1b[91mLogged Lago Object:\n%s\x1b[0m\n", returned_val)
        return returned_val

    def log_success_event(self, kwargs, response_obj, start_time, end_time):
        _url = os.getenv("LAGO_API_BASE")
        assert _url is not None and isinstance(_url, str), (

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Send the identity matching charge_by: user param / metadata user_id, team_id in metadata, or end-user id via litellm proxy user identification
  2. Switch LAGO_API_CHARGE_BY to the field you can reliably populate
  3. Ensure the proxy forwards these fields to the callback (metadata passthrough enabled)

Example fix

# before
litellm.completion(model='gpt-4o', messages=msgs)  # no user identity

# after
litellm.completion(
    model='gpt-4o',
    messages=msgs,
    user='user-123',
    metadata={'user_id': 'user-123'},
)
Defensive patterns

Strategy: validation

Validate before calling

import os

charge_by = os.getenv("LAGO_API_CHARGE_BY", "end_user_id")
identity = {"end_user_id": end_user_id, "user_id": user_id, "team_id": team_id}[charge_by]
if identity is None:
    raise ValueError(
        f"Request must carry a {charge_by}; cannot bill via Lago without it"
    )

Type guard

def has_billing_identity(charge_by: str, *, user_id, end_user_id, team_id) -> bool:
    return {
        "end_user_id": end_user_id,
        "user_id": user_id,
        "team_id": team_id,
    }.get(charge_by) is not None

Try / catch

try:
    event = build_lago_event(...)
except Exception as e:
    if "External Customer ID is not set" in str(e):
        # log-and-continue: billing should not break inference
        log.warning("Lago billing skipped: no identity for charge_by=%s", charge_by)
    else:
        raise

Prevention

When it happens

Trigger: LAGO_API_CHARGE_BY=user_id but the call carries no user param; charge_by=team_id without a team id in kwargs/metadata; default end_user_id with no end-user identified (no litellm_user_id / metadata end_user_id). Any of these yields None for the chosen field.

Common situations: Proxy deployments where clients don't send user identification; metadata keys not propagated (e.g. missing user / end_user_id in request metadata); charging by team_id while only user ids are supplied; testing with bare completion() calls.

Related errors


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