{"record":{"id":"fd626226e187fd37","repo":"BerriAI/litellm","slug":"external-customer-id-is-not-set-charge-by-charge","errorCode":null,"errorMessage":"External Customer ID is not set. Charge_by={charge_by}. User_id={user_id}. End_user_id={end_user_id}. Team_id={team_id}","messagePattern":"External Customer ID is not set\\. Charge_by=(.+?)\\. User_id=(.+?)\\. End_user_id=(.+?)\\. Team_id=(.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/integrations/lago.py","lineNumber":107,"sourceCode":"        if os.getenv(\"LAGO_API_CHARGE_BY\", None) is not None and isinstance(os.environ[\"LAGO_API_CHARGE_BY\"], str):\n            if os.environ[\"LAGO_API_CHARGE_BY\"] in [\n                \"end_user_id\",\n                \"user_id\",\n                \"team_id\",\n            ]:\n                charge_by = os.environ[\"LAGO_API_CHARGE_BY\"]\n            else:\n                raise Exception(\"invalid LAGO_API_CHARGE_BY set\")\n\n        if charge_by == \"end_user_id\":\n            external_customer_id = end_user_id\n        elif charge_by == \"team_id\":\n            external_customer_id = team_id\n        elif charge_by == \"user_id\":\n            external_customer_id = user_id\n\n        if external_customer_id is None:\n            raise Exception(\n                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}\"\n            )\n\n        returned_val: Final = {\n            \"event\": {\n                \"transaction_id\": str(uuid.uuid4()),\n                \"external_subscription_id\": external_customer_id,\n                \"code\": os.getenv(\"LAGO_API_EVENT_CODE\"),\n                \"properties\": {\"model\": model, \"response_cost\": cost, **usage},\n            }\n        }\n\n        verbose_logger.debug(\"\\x1b[91mLogged Lago Object:\\n%s\\x1b[0m\\n\", returned_val)\n        return returned_val\n\n    def log_success_event(self, kwargs, response_obj, start_time, end_time):\n        _url = os.getenv(\"LAGO_API_BASE\")\n        assert _url is not None and isinstance(_url, str), (","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/integrations/lago.py#L89-L125","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Send the identity matching charge_by: user param / metadata user_id, team_id in metadata, or end-user id via litellm proxy user identification","Switch LAGO_API_CHARGE_BY to the field you can reliably populate","Ensure the proxy forwards these fields to the callback (metadata passthrough enabled)"],"exampleFix":"# before\nlitellm.completion(model='gpt-4o', messages=msgs)  # no user identity\n\n# after\nlitellm.completion(\n    model='gpt-4o',\n    messages=msgs,\n    user='user-123',\n    metadata={'user_id': 'user-123'},\n)","handlingStrategy":"validation","validationCode":"import os\n\ncharge_by = os.getenv(\"LAGO_API_CHARGE_BY\", \"end_user_id\")\nidentity = {\"end_user_id\": end_user_id, \"user_id\": user_id, \"team_id\": team_id}[charge_by]\nif identity is None:\n    raise ValueError(\n        f\"Request must carry a {charge_by}; cannot bill via Lago without it\"\n    )","typeGuard":"def has_billing_identity(charge_by: str, *, user_id, end_user_id, team_id) -> bool:\n    return {\n        \"end_user_id\": end_user_id,\n        \"user_id\": user_id,\n        \"team_id\": team_id,\n    }.get(charge_by) is not None","tryCatchPattern":"try:\n    event = build_lago_event(...)\nexcept Exception as e:\n    if \"External Customer ID is not set\" in str(e):\n        # log-and-continue: billing should not break inference\n        log.warning(\"Lago billing skipped: no identity for charge_by=%s\", charge_by)\n    else:\n        raise","preventionTips":["Standardize on one identity field you can always populate, and set LAGO_API_CHARGE_BY to it","Require user/metadata fields on all completion calls via middleware","Treat billing failures as non-fatal to the inference path"],"tags":["lago","litellm","billing","configuration","identity"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}