[Bug]: Reused x-litellm-call-id silently drops spend-log rows
proxy
### 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 条评论