{"record":{"id":"8115e5927975e262","repo":"BerriAI/litellm","slug":"limit-must-be-an-integer","errorCode":null,"errorMessage":"limit must be an integer","messagePattern":"limit must be an integer","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"litellm/integrations/cloudzero/database.py","lineNumber":92,"sourceCode":"        FROM \"LiteLLM_DailyUserSpend\" dus\n        LEFT JOIN \"LiteLLM_VerificationToken\" vt ON dus.api_key = vt.token\n        LEFT JOIN \"LiteLLM_TeamTable\" tt ON vt.team_id = tt.team_id\n        LEFT JOIN \"LiteLLM_UserTable\" ut ON dus.user_id = ut.user_id\n        WHERE ($1::timestamptz IS NULL OR dus.updated_at >= $1::timestamptz)\n          AND ($2::timestamptz IS NULL OR dus.updated_at <= $2::timestamptz)\n        ORDER BY dus.date DESC, dus.created_at DESC\n        \"\"\"\n\n        params: Final[list[Any]] = [\n            start_time_utc,\n            end_time_utc,\n        ]\n\n        if limit is not None:\n            try:\n                params.append(int(limit))\n            except (TypeError, ValueError):\n                raise ValueError(\"limit must be an integer\")\n            query += \" LIMIT $3\"\n\n        try:\n            db_response: Final = await client.db.query_raw(query, *params)\n            # Convert the response to polars DataFrame with full schema inference\n            # This prevents schema mismatch errors when data types vary across rows\n            return pl.DataFrame(db_response, infer_schema_length=None)\n        except Exception as e:\n            raise Exception(f\"Error retrieving usage data: {e}\")\n","sourceCodeStart":74,"sourceCodeEnd":102,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/integrations/cloudzero/database.py#L74-L102","documentation":"ValueError from LiteLLMDatabase.get_usage_data when the limit parameter cannot be converted with int(limit) — either a TypeError (type has no int conversion, e.g. a list or None-like object passed despite the None check) or ValueError (a non-numeric string like 'all' or '1.5.2'). This is input validation before the SQL LIMIT clause is appended.","triggerScenarios":"Calling get_usage_data(limit='all'), limit='1.5.2', or limit=[100] — typically when the value arrives from a CLI flag, HTTP query param, or config file without prior type coercion. Floats like '1.5' fail int() too even though they look numeric.","commonSituations":"Script or scheduler config passing limit as a string from YAML/JSON; users typing 'unlimited'/'all'; API query params not cast to int at the boundary.","solutions":["Pass limit as an actual int, or omit it entirely for no limit","Coerce at the boundary: int(str_limit) inside your own try/except before calling","Reject non-numeric strings early with a clear 400-style error in your caller","For 'unlimited' semantics, pass limit=None, not a sentinel string"],"exampleFix":"# before\ndf = await db.get_usage_data(limit=request.args.get(\"limit\"))  # 'all' → ValueError\n\n# after\nraw = request.args.get(\"limit\")\nlimit = int(raw) if raw and raw.isdigit() else None\ndf = await db.get_usage_data(limit=limit)","handlingStrategy":"validation","validationCode":"def coerce_limit(raw) -> int | None:\n    if raw is None:\n        return None\n    try:\n        return int(raw)\n    except (TypeError, ValueError):\n        return None  # or raise your own 400 error","typeGuard":"def is_int_like_limit(raw) -> bool:\n    if raw is None:\n        return True\n    if isinstance(raw, bool):\n        return False\n    try:\n        int(raw)\n        return True\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    df = await db.get_usage_data(limit=raw_limit)\nexcept ValueError as e:\n    if \"limit must be an integer\" in str(e):\n        df = await db.get_usage_data(limit=None)\n    else:\n        raise","preventionTips":["Cast limit to int at the API/config boundary","Use None for unlimited rather than sentinel strings","Validate query params with pydantic before they reach the database layer"],"tags":["cloudzero","validation","type-error","database"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}