{"record":{"id":"a52696d19c5a073d","repo":"BerriAI/litellm","slug":"openmeter-user-is-required","errorCode":null,"errorMessage":"OpenMeter: user is required","messagePattern":"OpenMeter: user is required","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/integrations/openmeter.py","lineNumber":84,"sourceCode":"        # OPENMETER_TRUST_REQUEST_USER (default \"true\"): when set to \"false\",\n        # the request-supplied `user` field is ignored and the subject is\n        # resolved solely from the key-bound user_api_key_user_id. Proxies\n        # serving multi-tenant traffic enable this to prevent clients from\n        # forging attribution by setting `user` in the request body.\n        trust_request_user: Final = os.getenv(\"OPENMETER_TRUST_REQUEST_USER\", \"true\").lower() != \"false\"\n        user_param = kwargs.get(\"user\", None) if trust_request_user else None\n\n        # If no user provided directly, try to get it from token user_id\n        if user_param is None:\n            # Check if user_id is available from the API key metadata\n            litellm_params: Final = kwargs.get(\"litellm_params\", {})\n            metadata: Final = litellm_params.get(\"metadata\", {})\n            user_api_key_user_id: Final = metadata.get(\"user_api_key_user_id\", None)\n\n            if user_api_key_user_id is not None:\n                user_param = user_api_key_user_id\n            else:\n                raise Exception(\"OpenMeter: user is required\")\n\n        # Ensure subject is always a string for OpenMeter API\n        subject: Final = str(user_param)\n\n        return {\n            \"specversion\": \"1.0\",\n            \"type\": os.getenv(\"OPENMETER_EVENT_TYPE\", \"litellm_tokens\"),\n            \"id\": call_id,\n            \"time\": dt,\n            \"subject\": subject,\n            \"source\": \"litellm-proxy\",\n            \"data\": {\"model\": model, \"cost\": cost, **usage},\n        }\n\n    def log_success_event(self, kwargs, response_obj, start_time, end_time):\n        _url = os.getenv(\"OPENMETER_API_ENDPOINT\", \"https://openmeter.cloud\")\n        if _url.endswith(\"/\"):\n            _url += \"api/v1/events\"","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/integrations/openmeter.py#L66-L102","documentation":"When building the CloudEvent for OpenMeter, the logger must resolve a 'subject' (the user the tokens are attributed to). It first takes the request's user field, but only when OPENMETER_TRUST_REQUEST_USER is not 'false' (default is trusted); otherwise it falls back to the user_api_key_user_id bound to the virtual API key in metadata. If both are absent it raises Exception('OpenMeter: user is required'). This exists so OpenMeter metering always has a non-empty subject.","triggerScenarios":"Specific paths: (1) OPENMETER_TRUST_REQUEST_USER=false (set for multi-tenant proxies to stop clients forging attribution) while the request's virtual key has no user_id bound — e.g. calls made with the master key or an unbound virtual key; (2) trust enabled (default) but the SDK call omits user= and the key metadata has no user_api_key_user_id. The exception is raised inside log_success_event after every successful LLM call, so it surfaces as a logging-callback failure, not a failed LLM call.","commonSituations":"Hardening a proxy by setting OPENMETER_TRUST_REQUEST_USER=false without first binding user_id to every virtual key; testing with the sk- master key instead of a virtual key; SDK users calling completion(model=..., messages=...) with no user argument; teams whose keys were created before user attribution was introduced.","solutions":["Bind a user to the virtual key used for requests: litellm proxy admin create key with user_id (or update_key with user_id) so metadata.user_api_key_user_id resolves","Or pass user in the request: litellm.completion(..., user='user-123') / the user field in /chat/completions — works when OPENMETER_TRUST_REQUEST_USER is not 'false'","If you DO trust request-supplied user values, ensure OPENMETER_TRUST_REQUEST_USER is unset or 'true' so the request user is used","Audit for master-key or unbound-key traffic before enabling strict mode (OPENMETER_TRUST_REQUEST_USER=false)"],"exampleFix":"# before: trust disabled + unbound key -> 'OpenMeter: user is required'\nOPENMETER_TRUST_REQUEST_USER=false\n# key created without user_id; call has no user field\n\n# after: bind the user to the key\n# litellm-admin: curl -X POST /key/generate -d '{\"user_id\": \"user-123\", ...}'\nOPENMETER_TRUST_REQUEST_USER=false\n# or, if request users are trustworthy:\nOPENMETER_TRUST_REQUEST_USER=true  # and pass user='user-123' on each call","handlingStrategy":"validation","validationCode":"import os\n\n# Before enabling openmeter logging in strict mode, ensure every key in use carries a user.\ntrust = os.getenv('OPENMETER_TRUST_REQUEST_USER', 'true').lower() != 'false'\nif not trust:\n    # with trust off, every virtual key issuing traffic MUST have user_id bound\n    keys_without_user = [k for k in fetch_virtual_keys() if not k.get('user_id')]\n    assert not keys_without_user, f'bind user_id before strict mode: {keys_without_user}'\n\n# SDK side: always attribute the call\n# litellm.completion(model=..., messages=..., user='user-123')","typeGuard":null,"tryCatchPattern":"try:\n    litellm.completion(model='gpt-4o', messages=[...], user='user-123')\nexcept Exception as e:\n    # note: the raise happens in the logging hook; the LLM response may already exist.\n    if 'OpenMeter: user is required' in str(e):\n        # fix attribution (bind user_id / pass user) and retry once\n        ...","preventionTips":["Bind user_id to every virtual key at creation time so attribution never depends on client input","Wrap SDK calls with a helper that injects user= when the caller omits it","Before flipping OPENMETER_TRUST_REQUEST_USER to false, audit existing keys for missing user_id","Monitor proxy logs for the 'user is required' message to catch unattributed traffic early"],"tags":["openmeter","user-attribution","virtual-key","configuration","logging-callback"],"backgroundTag":"missing-required-argument","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}