{"record":{"id":"541321fb5591ef01","repo":"BerriAI/litellm","slug":"usage-is-required-got-usage-of-type-type-usage","errorCode":null,"errorMessage":"usage is required, got={usage} of type {type(usage)}","messagePattern":"usage is required, got=(.+?) of type (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/litellm_logging.py","lineNumber":4934,"sourceCode":"            )\n\n        usage: Final = response_obj.get(\"usage\", None) or {}\n        if usage is None or (not isinstance(usage, dict) and not isinstance(usage, Usage)):\n            return Usage(\n                prompt_tokens=0,\n                completion_tokens=0,\n                total_tokens=0,\n            )\n        elif isinstance(usage, Usage):\n            return usage\n        elif isinstance(usage, ResponseAPIUsage):\n            return ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage(usage)\n        elif isinstance(usage, dict):\n            if ResponseAPILoggingUtils._is_response_api_usage(usage):\n                return ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage(usage)\n            return Usage(**usage)\n\n        raise ValueError(f\"usage is required, got={usage} of type {type(usage)}\")\n\n    @staticmethod\n    def get_usage_as_dict(\n        response_obj: dict | None,\n        combined_usage_object: Usage | None = None,\n    ) -> dict:\n        \"\"\"\n        Like get_usage_from_response_obj but returns a plain dict, skipping\n        the Pydantic Usage construction on the hot path.\n        \"\"\"\n        _empty: Final[dict] = {\"prompt_tokens\": 0, \"completion_tokens\": 0, \"total_tokens\": 0}\n        if combined_usage_object is not None:\n            return combined_usage_object.model_dump()\n        if not response_obj:\n            return _empty\n        _raw: Final = response_obj.get(\"usage\", None)\n        if _raw is None:\n            return _empty","sourceCodeStart":4916,"sourceCodeEnd":4952,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/litellm_core_utils/litellm_logging.py#L4916-L4952","documentation":"ResponseAPILoggingUtils.get_usage_from_response_obj normalizes the usage field and accepts only a Usage object, an OpenAI Responses-API ResponseAPIUsage object, or a plain dict. Any other type (None that isn't pre-handled, a string, a pydantic model from another library, an arbitrary object) reaches the terminal raise. The error exists to prevent silent zero-usage logging when callers pass malformed usage payloads.","triggerScenarios":"Custom callbacks/handlers (or downstream code overriding response objects) passing usage as something other than Usage/ResponseAPIUsage/dict — e.g. usage=None bypassing the falsy branch because an earlier branch already consumed the empty case, usage as a JSON string, or a provider-specific usage model not yet transformed.","commonSituations":"Adding new providers whose raw usage objects are forwarded untransformed; mocking responses in tests with usage as a string or MagicMock; adapter code copying usage between response types.","solutions":["Convert before logging: pass usage as a plain dict (Usage(**d) is constructed for you) or a litellm.Usage instance","In provider adapters, transform provider-specific usage into Usage/ResponseAPIUsage before the response reaches logging","In tests, replace MagicMocks for usage with real Usage objects or dicts"],"exampleFix":"# before\nusage = '{\"prompt_tokens\": 1}'          # str -> ValueError\n\n# after\nusage = {\"prompt_tokens\": 1, \"completion_tokens\": 2, \"total_tokens\": 3}\n# or\nfrom litellm import Usage\nusage = Usage(prompt_tokens=1, completion_tokens=2, total_tokens=3)","handlingStrategy":"type-guard","validationCode":"from litellm import Usage\n\ndef normalize_usage(u):\n    if isinstance(u, Usage) or isinstance(u, dict):\n        return u\n    if u is None:\n        return {'prompt_tokens': 0, 'completion_tokens': 0, 'total_tokens': 0}\n    raise TypeError(f'unsupported usage type: {type(u)}')","typeGuard":"from litellm import Usage\n\ndef is_supported_usage(u) -> bool:\n    return isinstance(u, (Usage, dict))  # ResponseAPIUsage also accepted upstream","tryCatchPattern":null,"preventionTips":["In adapters, convert provider usage objects to litellm.Usage or a dict before logging","Use real Usage objects in tests, never strings or mocks","Centralize usage transformation in one helper per provider"],"tags":["litellm","logging","usage","type-validation","callbacks"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}