快速结论:当客户端通过 HTTP 请求头 x-litellm-tags 传递标签时,LiteLLM 的标签预算检查(_tag_max_budget_check)会静默跳过,导致即使标签累计消费超过 max_budget,请求仍被放行(HTTP 200)。优先排查:确认你使用的是请求头传标签方式,如果是,则问题必然存在;将标签改为请求体({"tags": [...]})传递可以绕过此问题。
问题场景
在 LiteLLM(版本 v1.83.14-stable.patch.2)中,当满足以下条件时触发:
- 生成的虚拟密钥(virtual key)设置了
metadata.allow_client_tags: true - 客户端通过
x-litellm-tags请求头传递标签 - 该标签关联了预算(
max_budget),并且累计消费已超过预算上限
预期行为:LiteLLM 应返回 HTTP 400 错误(budget_exceeded)。实际行为:返回 HTTP 200,请求正常处理,标签消费继续增长。
报错原文
# 实际无报错,请求被静默放行
# 预期报错:
HTTP 400
{"error": {"type": "budget_exceeded", "message": "Budget has been exceeded! Tag=tenant:acme ..."}}
原因分析
授权链中的执行顺序问题导致。LiteLLM 的预算检查函数 _tag_max_budget_check 在 litellm/proxy/auth/auth_checks.py 中调用,该函数通过 get_tags_from_request_body(request_body)(位于 litellm/proxy/common_utils/http_parsing_utils.py)获取标签,但该函数只解析请求体中的 tags 字段和 metadata.tags 字段。
而 x-litellm-tags 请求头的解析和合并发生在 LiteLLMProxyRequestSetup.add_request_tag_to_metadata(位于 litellm/proxy/litellm_pre_call_utils.py),此函数在 授权链完成后 才执行(在 add_litellm_data_to_request 内部)。因此当预算检查运行时,get_tags_from_request_body 返回空列表([]),预算检查循环被跳过,静默放行所有请求。
根本原因:预算检查基于请求体读取标签,但请求头标签在授权之后才合并,形成了一个“时间差”漏洞。
环境排查
- LiteLLM 版本:确认是否
v1.83.14-stable.patch.2或相似版本(该问题在旧版本中可能存在更久) - 部署方式:Docker Compose 部署(附带 Postgres + Redis)或自定义部署
- Key 配置:检查虚拟密钥是否设置了
metadata.allow_client_tags: true - 预算配置:确认标签已关联预算(
budget_id)且已超支 - 请求方式:确认标签是通过
x-litellm-tags请求头传递,而不是请求体
解决步骤
- 临时规避方案:将标签改为通过请求体传递。例如:
# 当前使用请求头(会触发问题) -H "x-litellm-tags: tenant:acme" # 改为请求体 -d '{"tags": ["tenant:acme"], ...}'注意:如果必须通过请求头传递,此方案不适用。
- 官方修复建议(两个等效方案):
- 最小化修复(Option A,推荐优先尝试):修改
_tag_max_budget_check函数(位于litellm/proxy/auth/auth_checks.py),增加对x-litellm-tags请求头的直接读取,并与请求体中的标签合并。大致逻辑:async def _tag_max_budget_check( request_body: dict, request_headers: Optional[dict], # <-- 新增参数 prisma_client: ..., ): tags = get_tags_from_request_body(request_body=request_body) if request_headers and request_headers.get("x-litellm-tags"): header_tags = [t.strip() for t in request_headers["x-litellm-tags"].split(",")] tags = list({*tags, *header_tags}) # 合并去重 if not tags: return # ... 原有预算检查逻辑 - 更干净的修复(Option B):将
add_request_tag_to_metadata函数(或一个仅处理请求头标签的变体)提前到common_checks之前执行,这样请求体中的metadata.tags已在预算检查时完成合并。 - 等待官方发布修复版本:检查后续 LiteLLM 发布版本,确认该问题是否被解决。如果你自行应用了上述补丁,请保留原始代码备份。
验证方法
- 重现验证:按照 Issue 中的 Reproduce 步骤操作(创建密钥 → 创建预算 + 标签 → 通过请求头发送请求使消费超支 → 再次请求),确认修复前返回 HTTP 200,修复后返回 HTTP 400。
- 对比测试:同时使用请求头传标签和请求体传标签两种方式,确认两者在超预算时均返回
budget_exceeded。 - 检查标签消费:通过
/spend/tags端点查看标签累计消费,确认消费不再无限增长(在预算上限处被拦截)。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


