ITADN

[Bug]: Reused x-litellm-call-id silently drops spend-log rows

#35563Openemerzon 创建于 19 天前
proxy
E
emerzoncommented
### Check for existing issues - [x] I searched open and closed issues before filing - [x] [#28562](https://github.com/BerriAI/litellm/issues/28562) is related to passthrough call ID correlation but does not cover primary-key collisions or dropped rows ### What happened? The proxy accepts a caller-provided `x-litellm-call-id` and uses it as the fallback `LiteLLM_SpendLogs.request_id` whenever the response has no provider ID, including failure and passthrough-style logging paths `LiteLLM_SpendLogs.request_id` is the table primary key. Spend-log batches are inserted with `create_many(..., skip_duplicates=True)`, so a repeated call ID does not produce an error or a second row. It is silently skipped A client can therefore reuse one header value across many requests and retain only the first per-request spend-log row. Aggregate key, user, and team spend updates use separate queues, so this does not directly bypass aggregate accounting, but it does make the request audit trail, per-request cost lookup, and analytics incomplete The trace identifier and durable spend-log identifier currently have incompatible trust and uniqueness requirements. The trace ID is intentionally client-controlled, while the spend-log primary key must be server-generated and unique ### Steps to Reproduce 1. Send two requests that reach a logging path without a provider response ID, using the same `x-litellm-call-id: repeated-id` 2. Wait for the spend-log queue to flush 3. Query `/spend/logs?request_id=repeated-id` 4. Observe that only one row exists even though both requests reached spend tracking The ID selection itself can be reproduced without a provider call ```python from litellm.proxy.spend_tracking.spend_tracking_utils import get_spend_logs_id kwargs = {"litellm_call_id": "repeated-id"} assert get_spend_logs_id("pass_through_endpoint", {}, kwargs) == "repeated-id" assert get_spend_logs_id("pass_through_endpoint", {}, kwargs) == "repeated-id" ``` The later insert uses `skip_duplicates=True`, so the second row is discarded as an expected duplicate ### Expected behavior Every request that reaches spend tracking receives a unique durable row ID independent of caller-controlled trace metadata A suitable shape is a server-generated spend-log primary key plus `litellm_call_id` as a separate indexed correlation field. If compatibility requires exposing the call ID through `request_id`, the stored identity still needs a server namespace or unique suffix ### Relevant code - [Header assignment](https://github.com/BerriAI/litellm/blob/b1fd20f4cdc8f7a9b4e0c886a277465aedb05ec8/litellm/proxy/common_request_processing.py#L1297) - [Spend-log ID fallback](https://github.com/BerriAI/litellm/blob/b1fd20f4cdc8f7a9b4e0c886a277465aedb05ec8/litellm/proxy/spend_tracking/spend_tracking_utils.py#L175-L183) - [Primary-key schema](https://github.com/BerriAI/litellm/blob/b1fd20f4cdc8f7a9b4e0c886a277465aedb05ec8/schema.prisma#L607-L610) - [Duplicate-skipping insert](https://github.com/BerriAI/litellm/blob/b1fd20f4cdc8f7a9b4e0c886a277465aedb05ec8/litellm/proxy/utils.py#L5782-L5813) ### What part of LiteLLM is this about? Proxy ### What LiteLLM version are you on? v1.96.0, commit `b1fd20f4cdc8f7a9b4e0c886a277465aedb05ec8`
0 条评论