快速结论:该报错通常出现在 vLLM V1 引擎开启动态批处理、同时混用带 allowed_token_ids 受限请求与无限制请求时,InputBatch.condense() 会把已释放行的 stale allowed_token_ids_mask_cpu_tensor 残留下来,并被后续无限制请求复用,导致错误的白名单被应用到不该受限的请求上;优先排查 allowed_token_ids 与批内请求增删/压缩(condense)相关的状态复用路径。
适用环境:Issue 中已确认的环境为 GCP a2-highgpu-1g、1x NVIDIA A100-SXM4-40GB、驱动 580.159.03、vLLM 源码 4bfa0f2b1458be320fa39c6fa54be5f83cef2444、vLLM 版本 0.1.dev1+g4bfa0f2b1、PyTorch 2.11.0+cu130、CUDA visible True,安装方式为 editable source install 配合最新 main/nightly wheel 的预编译产物。
最快修复方案:暂无确认的一步修复方案。Issue 正文仅给出聚焦复现脚本以确认 stale 状态可达,评论区指出 condense() 由 #43931 处理、swap_states() 由 #48419 处理,但本 Issue 本身并未合并一个可直接套用的补丁。
注意事项:该问题不只是 condense() 一处;评论指出 InputBatch.swap_states() 中存在第二处同类 bug,原因是二维张量整数索引返回的是 view 而非 copy,元组交换会先后覆盖导致两行坍缩为原始 i2 内容,从而静默丢失某一请求的 allowed_token_ids 限制。该路径经 reorder_batch(MLA / Mamba / GDN / FlashInfer decode-prefill split)可达。1D _cpu 数组(整数索引返回标量 copy)以及已使用安全模式的 token_ids_cpu/is_token_ids 不受影响。
问题场景
在 vLLM V1 InputBatch 上进行动态批处理、请求动态增删时触发。具体为:一个带 allowed_token_ids 的受限请求在 condense() 中被从高行号移动到低行号后,其原行仍然保留 true_count=15, false_ids=[13] 的旧掩码;随后一个无限制请求复用该行,而 add_request() 只在 sampling_params.allowed_token_ids 被设置时才写入 allowed_token_ids_mask_cpu_tensor[req_index],因此不会覆盖旧值。当另有活动请求仍带 allowed_token_ids 时,_make_sampling_metadata() 会把 CPU 掩码的活动前缀拷贝到 GPU,Sampler.apply_logits_processors() 于是把这条 stale 白名单应用到了无限制请求上。
报错原文
[Bug] V1 InputBatch condense can leak stale allowed_token_ids mask to recycled row
"req3_metadata_row": {"false_count": 1, "false_ids": [13], "true_count": 15},
"stale_mask_reachable": true
原因分析
最可能的原因是 InputBatch.condense() 在把受限请求下移时,只更新了目标行,未清除源行的 allowed_token_ids_mask_cpu_tensor,从而留下 stale 掩码行;同时 add_request() 在无 allowed_token_ids 时不写入该行,使复用该行的无限制请求继承了旧限制。另一类独立但同族的根因出现在 swap_states():对二维张量行做整数索引得到的是 view 而非 copy,元组赋值交换会先写坏其中一行,导致两行都等于原来的 i2 内容,丢失被移到 i2 请求的限制。该 swap_states() 路径的根因由评论给出,且已有 #48419 的修复与回归测试。
环境排查
- 确认 vLLM 是否为受影响的源码版本(Issue 报告于
4bfa0f2b1458be320fa39c6fa54be5f83cef2444,版本0.1.dev1+g4bfa0f2b1)。 - 确认 PyTorch 版本(Issue 为
2.11.0+cu130)与 CUDA 是否可见。 - 确认 GPU 型号(Issue 为
NVIDIA A100-SXM4-40GB)与驱动版本(580.159.03)。 - 确认是否启用动态批处理、是否存在请求被
condense()压缩或多请求swap_states()重排的场景。 - 确认同一批次中是否同时存在带
allowed_token_ids的受限请求与无限制请求。 - 确认是否走过
reorder_batch的 MLA / Mamba / GDN / FlashInfer decode-prefill split 路径(这是swap_states()分支的可达条件)。
解决步骤
- 使用 Issue 中的聚焦复现脚本确认当前代码是否存在 stale 掩码:
python repro_allowed_token_ids_mask_state.py --device cuda:0 --expect stale。 - 查看输出 JSON 中的
req3_metadata_row与stale_mask_reachable;若出现false_count=1, false_ids=[13], true_count=15且stale_mask_reachable=true,即可确认该路径上的 stale 掩码问题。 - 针对
condense()半部分,关注 #43931 的修复方向(清理被移动行的 stale 掩码)。 - 针对
swap_states()半部分,关注 #48419 的修复方向(把二维掩码行的元组交换改为 fancy-index copy,与上方已有的安全is_token_ids交换写法一致)。 - 更新到包含上述修复的 vLLM 版本,或用可用参数复现前先确认
allowed_token_ids_mask_cpu_tensor在condense()与swap_states()两条路径上的清理/拷贝行为。 - 在无确认修复可用时,可优先尝试避免在同一批中混用带与不带
allowed_token_ids的请求、或避免触发condense()与swap_states()的请求增删/重排,作为临时规避手段。
验证方法
以 --expect fixed 运行同一复现脚本,若输出 req3_metadata_row 变为 false_count=16, false_ids=[0..15], true_count=0 且 stale_mask_reachable=false,即表明该行不再保留 stale 白名单,问题已被修复。同时在真实服务中确认无限制请求不会再被应用其他请求的 allowed_token_ids 限制。
参考来源
相关 Issue/PR:#43931(condense() 半部分修复)、#48419(swap_states() 修复与回归测试)。
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。

![Misc. bug: Vulkan ARGSORT ne=[2048,1,1,1] only sorts half of the array on some devices](https://www.chat-gpts.plus/wp-content/uploads/2026/09/29431-0e9cbdaa-768x403.jpg)
![[Bug] Qwen4Exp QSA indexer: per-chunk logits buffer grows with max_seq_len, caching allocator keeps every size, device OOM/hang on unified-m](https://www.chat-gpts.plus/wp-content/uploads/2026/09/56457-d01dde83-768x403.jpg)