Your current environment
The output of python collect_env.py
Collecting environment information...
==============================
System Info
==============================
OS : Ubuntu 22.04.5 LTS (x86_64)
GCC version : (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0
Clang version : Could not collect
CMake version : Could not collect
Libc version : glibc-2.35
==============================
PyTorch Info
==============================
PyTorch version : 2.11.0+cu130
Is debug build : False
CUDA used to build PyTorch : 13.0
ROCM used to build PyTorch : N/A
XPU used to build PyTorch : N/A
==============================
Python Environment
==============================
Python version : 3.12.13 (main, Mar 4 2026, 09:23:07) [GCC 11.4.0] (64-bit runtime)
Python platform : Linux-5.15.0-177-generic-x86_64-with-glibc2.35
==============================
CUDA / GPU Info
==============================
Is CUDA available : True
CUDA runtime version : 13.0.88
CUDA_MODULE_LOADING set to :
GPU models and configuration : GPU 0: NVIDIA RTX 5000 Ada Generation
Nvidia driver version : 580.65.06
cuDNN version : Could not collect
HIP runtime version : N/A
MIOpen runtime version : N/A
Is XNNPACK available : True
==============================
CPU Info
==============================
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 46 bits physical, 57 bits virtual
Byte Order: Little Endian
CPU(s): 64
On-line CPU(s) list: 0-63
Vendor ID: GenuineIntel
Model name: Intel(R) Xeon(R) Gold 6426Y
CPU family: 6
Model: 143
Thread(s) per core: 2
Core(s) per socket: 16
Socket(s): 2
Stepping: 8
BogoMIPS: 5000.00
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities ibpb_exit_to_user
Virtualization: VT-x
L1d cache: 1.5 MiB (32 instances)
L1i cache: 1 MiB (32 instances)
L2 cache: 64 MiB (32 instances)
L3 cache: 75 MiB (2 instances)
NUMA node(s): 4
NUMA node0 CPU(s): 0-7,32-39
NUMA node1 CPU(s): 8-15,40-47
NUMA node2 CPU(s): 16-23,48-55
NUMA node3 CPU(s): 24-31,56-63
Vulnerability Gather data sampling: Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit: Not affected
Vulnerability L1tf: Not affected
Vulnerability Mds: Not affected
Vulnerability Meltdown: Not affected
Vulnerability Mmio stale data: Not affected
Vulnerability Reg file data sampling: Not affected
Vulnerability Retbleed: Not affected
Vulnerability Spec rstack overflow: Not affected
Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl and seccomp
Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2: Mitigation; Enhanced / Automatic IBRS; IBPB conditional; PBRSB-eIBRS SW sequence; BHI BHI_DIS_S
Vulnerability Srbds: Not affected
Vulnerability Tsa: Not affected
Vulnerability Tsx async abort: Not affected
Vulnerability Vmscape: Mitigation; IBPB before exit to userspace
==============================
Versions of relevant libraries
==============================
[pip3] flashinfer-python==0.6.12
[pip3] numpy==2.2.6
[pip3] nvidia-cublas==13.1.0.3
[pip3] nvidia-cuda-cccl==13.3.3.3.1
[pip3] nvidia-cuda-crt==13.3.33
[pip3] nvidia-cuda-cupti==13.0.85
[pip3] nvidia-cuda-nvcc==13.2.78
[pip3] nvidia-cuda-nvrtc==13.0.88
[pip3] nvidia-cuda-runtime==13.0.96
[pip3] nvidia-cuda-tileiras==13.2.78
[pip3] nvidia-cudnn-cu13==9.19.0.56
[pip3] nvidia-cudnn-frontend==1.25.0
[pip3] nvidia-cufft==12.0.0.61
[pip3] nvidia-cufile==1.15.1.6
[pip3] nvidia-curand==10.4.0.35
[pip3] nvidia-cusolver==12.0.4.66
[pip3] nvidia-cusparse==12.6.3.3
[pip3] nvidia-cusparselt-cu13==0.8.0
[pip3] nvidia-cutlass-dsl==4.5.2
[pip3] nvidia-cutlass-dsl-libs-base==4.5.2
[pip3] nvidia-cutlass-dsl-libs-cu13==4.5.2
[pip3] nvidia-ml-py==13.610.43
[pip3] nvidia-nccl-cu13==2.28.9
[pip3] nvidia-nvjitlink==13.0.88
[pip3] nvidia-nvshmem-cu13==3.4.5
[pip3] nvidia-nvtx==13.0.85
[pip3] nvidia-nvvm==13.2.78
[pip3] pyzmq==27.1.0
[pip3] tokenspeed-triton==3.7.10.post20260531
[pip3] torch==2.11.0+cu130
[pip3] torch_c_dlpack_ext==0.1.5
[pip3] torchaudio==2.11.0+cu130
[pip3] torchvision==0.26.0+cu130
[pip3] transformers==5.12.1
[pip3] triton==3.6.0
[conda] Could not collect
==============================
vLLM Info
==============================
ROCM Version : Could not collect
vLLM Version : 0.24.0
vLLM Build Flags:
CUDA Archs: 7.5 8.0 8.6 8.9 9.0 10.0 12.0; ROCm: Disabled; XPU: Disabled
GPU Topology:
GPU0 CPU Affinity NUMA Affinity GPU NUMA ID
GPU0 X 0-7,32-39 0 N/A
Legend:
X = Self
SYS = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
PHB = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
PXB = Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
PIX = Connection traversing at most a single PCIe bridge
NV# = Connection traversing a bonded set of # NVLinks
==============================
Environment Variables
==============================
NVIDIA_VISIBLE_DEVICES=void
NVIDIA_REQUIRE_CUDA=cuda>=13.0 brand=unknown,driver>=535,driver<536 brand=grid,driver>=535,driver<536 brand=tesla,driver>=535,driver<536 brand=nvidia,driver>=535,driver<536 brand=quadro,driver>=535,driver<536 brand=quadrortx,driver>=535,driver<536 brand=nvidiartx,driver>=535,driver<536 brand=vapps,driver>=535,driver<536 brand=vpc,driver>=535,driver<536 brand=vcs,driver>=535,driver<536 brand=vws,driver>=535,driver<536 brand=cloudgaming,driver>=535,driver<536 brand=unknown,driver>=550,driver<551 brand=grid,driver>=550,driver<551 brand=tesla,driver>=550,driver<551 brand=nvidia,driver>=550,driver<551 brand=quadro,driver>=550,driver<551 brand=quadrortx,driver>=550,driver<551 brand=nvidiartx,driver>=550,driver<551 brand=vapps,driver>=550,driver<551 brand=vpc,driver>=550,driver<551 brand=vcs,driver>=550,driver<551 brand=unknown,driver>=565,driver<566 brand=grid,driver>=565,driver<566 brand=tesla,driver>=565,driver<566 brand=nvidia,driver>=565,driver<566 brand=quadro,driver>=565,driver<566 brand=quadrortx,driver>=565,driver<566 brand=nvidiartx,driver>=565,driver<566 brand=vapps,driver>=565,driver<566 brand=vpc,driver>=565,driver<566 brand=vcs,driver>=565,driver<566 brand=vws,driver>=565,driver<566 brand=cloudgaming,driver>=565,driver<566 brand=unknown,driver>=570,driver<571 brand=grid,driver>=570,driver<571 brand=tesla,driver>=570,driver<571 brand=nvidia,driver>=570,driver<571 brand=quadro,driver>=570,driver<571 brand=quadrortx,driver>=570,driver<571 brand=nvidiartx,driver>=570,driver<571 brand=vapps,driver>=570,driver<571 brand=vpc,driver>=570,driver<571 brand=vcs,driver>=570,driver<571 brand=unknown,driver>=575,driver<576 brand=grid,driver>=575,driver<576 brand=tesla,driver>=575,driver<576 brand=nvidia,driver>=575,driver<576 brand=quadro,driver>=575,driver<576 brand=quadrortx,driver>=575,driver<576 brand=nvidiartx,driver>=575,driver<576 brand=vapps,driver>=575,driver<576 brand=vpc,driver>=575,driver<576 brand=vcs,driver>=575,driver<576 brand=vws,driver>=575,driver<576 brand=cloudgaming,driver>=575,driver<576
TORCH_CUDA_ARCH_LIST=7.5 8.0 8.6 8.9 9.0 10.0 12.0
NVIDIA_DRIVER_CAPABILITIES=compute,utility
VLLM_USAGE_SOURCE=production-docker-image
CUDA_VERSION=13.0.2
VLLM_ENABLE_CUDA_COMPATIBILITY=0
LD_LIBRARY_PATH=/usr/local/nvidia/lib64:/usr/local/cuda/lib64:/usr/local/nvidia/lib:/usr/local/nvidia/lib64:/usr/local/cuda/lib64
NVIDIA_CTK_LIBCUDA_DIR=/usr/lib/x86_64-linux-gnu
PYTORCH_NVML_BASED_CUDA_CHECK=1
TORCHINDUCTOR_COMPILE_THREADS=1
TORCHINDUCTOR_CACHE_DIR=/tmp/torchinductor_root
🐛 Describe the bug
Summary
vllm.multimodal.evs.recompute_mrope_positions() incorrectly classifies a
chunked-prefill cursor that starts on the first media token immediately after a
vision_start token.
The problematic boundary is:
previous prefill chunk current prefill chunk
... TEXT VISION_START | MEDIA MEDIA MEDIA ...
^
num_computed_tokens
At this boundary, no media token has been counted as previously computed, but
the cursor already points to the current media. The implementation uses only
cumulative media-token counts to determine whether the cursor is inside a media
block. Because those counts are equal at this exact boundary, it searches for a
new vision_start at or after the cursor.
This produces two outcomes:
- If this is the last media item, the candidate tensor is empty and indexing
[0] raises a Python IndexError. In the serving path, the unhandled
exception terminates EngineCore.
- If another media item follows, its future
vision_start is selected. No
exception is raised, but the current media's M-RoPE metadata is applied to
the future media block.
The second outcome is a silent position-mapping error. A single request with a
single image is sufficient for the crash; request batching and multi-image
input are not required.
This was reproduced in pure vLLM without third-party source patches. The helper
is exercised when multimodal pruning changes the media-token layout and M-RoPE
must be recomputed. A pruning-disabled request does not normally enter this
path.
Minimal CPU reproduction
The failure can be reproduced without a GPU, model weights, images, or external
data. Run the script first with WITH_FUTURE_MEDIA = False, then set it to
True to reproduce the second outcome.
import torch
from vllm.multimodal.evs import recompute_mrope_positions
TEXT = 1
VISION_START = 777
VISION_END = 778
IMAGE = 999
VIDEO = 888
WITH_FUTURE_MEDIA = False
# Qwen3-VL image metadata: t, h, w, max_width, dummy_zero.
IMAGE_POSITIONS = torch.tensor(
[
[0, 0, 0, 0],
[0, 0, 1, 1],
[0, 1, 0, 1],
[2, 2, 2, 2],
[0, 0, 0, 0],
]
)
EXPECTED_FIRST_IMAGE = torch.tensor(
[
[2, 2, 2, 2],
[2, 2, 3, 3],
[2, 3, 2, 3],
]
)
input_tokens = [
TEXT,
VISION_START,
IMAGE,
IMAGE,
IMAGE,
IMAGE,
VISION_END,
TEXT,
]
if WITH_FUTURE_MEDIA:
input_tokens.extend(
[
VISION_START,
IMAGE,
IMAGE,
IMAGE,
IMAGE,
VISION_END,
TEXT,
]
)
input_ids = torch.tensor(input_tokens)
initial_positions = torch.arange(len(input_tokens)).expand(3, -1).clone()
positions, _ = recompute_mrope_positions(
input_ids=input_ids,
multimodal_positions=[IMAGE_POSITIONS],
mrope_positions=initial_positions,
num_computed_tokens=2, # TEXT VISION_START | first IMAGE
vision_start_token_id=VISION_START,
image_token_id=IMAGE,
video_token_id=VIDEO,
)
print("actual first image:\n", positions[:, 2:6])
print("expected first image:\n", EXPECTED_FIRST_IMAGE)
torch.testing.assert_close(positions[:, 2:6], EXPECTED_FIRST_IMAGE)
With WITH_FUTURE_MEDIA = False, unmodified vLLM 0.24.0 raises:
Traceback (most recent call last):
File "<reproduction script>", line 55, in <module>
File "/usr/local/lib/python3.12/dist-packages/vllm/multimodal/evs.py", line 298, in recompute_mrope_positions
next_vision_start_token = vision_start_indices[
^^^^^^^^^^^^^^^^^^^^^
IndexError: index 0 is out of bounds for dimension 0 with size 0
With WITH_FUTURE_MEDIA = True, it does not raise IndexError, but produces:
actual first image:
tensor([[2, 3, 4, 5],
[2, 3, 4, 5],
[2, 3, 4, 5]])
expected first image:
tensor([[2, 2, 2, 2],
[2, 2, 3, 3],
[2, 3, 2, 3]])
AssertionError: Tensor-likes are not equal!
Mismatched elements: 8 / 12 (66.7%)
Greatest absolute difference: 3 at index (0, 3)
Greatest relative difference: 1.5 at index (0, 3)
The first image remains sequential because its supplied positions were applied
to the later image block.
Serving impact validation
The same last-media boundary was also reproduced through the full pure-vLLM
serving path:
Container: vllm/vllm-openai:v0.24.0
Model: Qwen/Qwen3-VL-4B-Instruct
GPU: NVIDIA RTX 5000 Ada Generation, 30,712 MiB
Multimodal pruning: --video-pruning-rate 0.5
Chunked prefill: enabled
--max-num-batched-tokens 128
--max-model-len 4096
Prompt length: 176 tokens
Token index 127: vision_start
Token index 128: first image_pad
Scheduler state: num_computed_tokens=128
Although this option is named video_pruning_rate, it enables the current
Qwen3-VL multimodal-pruning path that also attaches recompute metadata to image
embeddings.
The request returned HTTP 500 and EngineCore terminated. The relevant pure-vLLM
call path was:
File "vllm/v1/worker/gpu_model_runner.py", line 3188, in _gather_mm_embeddings
self.model.recompute_mrope_positions(
File "vllm/model_executor/models/qwen3_vl.py", line 2685, in recompute_mrope_positions
return self._recompute_mrope_positions(
File "vllm/model_executor/models/qwen3_vl.py", line 2731, in _recompute_mrope_positions
positions, mrope_positions_delta = recompute_mrope_positions(
File "vllm/multimodal/evs.py", line 298, in recompute_mrope_positions
next_vision_start_token = vision_start_indices[
IndexError: index 0 is out of bounds for dimension 0 with size 0
This is a Python tensor-indexing exception, not a CUDA kernel error.
Root cause
The relevant branch is:
in_the_middle_of_media = (
seen_mm_tokens > seem_mm_tokens_before_last_vision_start
)
if not in_the_middle_of_media:
next_vision_start_token = vision_start_indices[
vision_start_indices >= num_computed_tokens
][0]
At VISION_START | MEDIA:
seen_mm_tokens = 0
media tokens before the last vision_start = 0
0 > 0 = false
The preceding vision_start belongs to the media at the cursor, but the
count-only condition loses that state. The cursor token itself provides the
missing information: media_mask[num_computed_tokens] is true at this
boundary.
Expected behavior
- A cursor beginning on the first media token should use the closest preceding
vision_start.
- A last or single media item should not search an empty future-marker set.
- A current media item should not be associated with a later media item's
marker.
- The supplied M-RoPE metadata should be written to the media block beginning
at the current cursor.
A focused correction would retain the existing count logic while recognizing
that a cursor currently pointing to a media token belongs to the preceding
vision_start:
in_the_middle_of_media = (
seen_mm_tokens > seem_mm_tokens_before_last_vision_start
or (
num_computed_tokens < N
and media_mask[num_computed_tokens].item()
)
)
Version scope
The issue was reproduced on the official vLLM 0.24.0 image. The same count-only
branch and unguarded future-marker indexing are still present in upstream
main at commit dc9f845ddc54c1df38fdbce5afe03f9fd15813bd (checked on
2026-07-16).
Related work
Related PR: #38888
At its current head (d093d3037350eb7c9de1d149f9311432de2e0adb), PR #38888
adds a fallback only when no future vision_start candidate exists. That
prevents the last-media exception. However, when a future marker exists, it is
still selected, so the future-media reproduction above continues to associate
the current media metadata with the later media block.
I searched open and closed issues and pull requests using
recompute_mrope_positions, vision_start_indices, chunked prefill mrope,
IndexError evs, multimodal pruning, and EVS, and did not find an exact
duplicate.
Before submitting a new issue...
Your current environment
The output of
python collect_env.py🐛 Describe the bug
Summary
vllm.multimodal.evs.recompute_mrope_positions()incorrectly classifies achunked-prefill cursor that starts on the first media token immediately after a
vision_starttoken.The problematic boundary is:
At this boundary, no media token has been counted as previously computed, but
the cursor already points to the current media. The implementation uses only
cumulative media-token counts to determine whether the cursor is inside a media
block. Because those counts are equal at this exact boundary, it searches for a
new
vision_startat or after the cursor.This produces two outcomes:
[0]raises a PythonIndexError. In the serving path, the unhandledexception terminates EngineCore.
vision_startis selected. Noexception is raised, but the current media's M-RoPE metadata is applied to
the future media block.
The second outcome is a silent position-mapping error. A single request with a
single image is sufficient for the crash; request batching and multi-image
input are not required.
This was reproduced in pure vLLM without third-party source patches. The helper
is exercised when multimodal pruning changes the media-token layout and M-RoPE
must be recomputed. A pruning-disabled request does not normally enter this
path.
Minimal CPU reproduction
The failure can be reproduced without a GPU, model weights, images, or external
data. Run the script first with
WITH_FUTURE_MEDIA = False, then set it toTrueto reproduce the second outcome.With
WITH_FUTURE_MEDIA = False, unmodified vLLM 0.24.0 raises:With
WITH_FUTURE_MEDIA = True, it does not raiseIndexError, but produces:The first image remains sequential because its supplied positions were applied
to the later image block.
Serving impact validation
The same last-media boundary was also reproduced through the full pure-vLLM
serving path:
Although this option is named
video_pruning_rate, it enables the currentQwen3-VL multimodal-pruning path that also attaches recompute metadata to image
embeddings.
The request returned HTTP 500 and EngineCore terminated. The relevant pure-vLLM
call path was:
This is a Python tensor-indexing exception, not a CUDA kernel error.
Root cause
The relevant branch is:
At
VISION_START | MEDIA:The preceding
vision_startbelongs to the media at the cursor, but thecount-only condition loses that state. The cursor token itself provides the
missing information:
media_mask[num_computed_tokens]is true at thisboundary.
Expected behavior
vision_start.marker.
at the current cursor.
A focused correction would retain the existing count logic while recognizing
that a cursor currently pointing to a media token belongs to the preceding
vision_start:Version scope
The issue was reproduced on the official vLLM 0.24.0 image. The same count-only
branch and unguarded future-marker indexing are still present in upstream
mainat commitdc9f845ddc54c1df38fdbce5afe03f9fd15813bd(checked on2026-07-16).
Related work
Related PR: #38888
At its current head (
d093d3037350eb7c9de1d149f9311432de2e0adb), PR #38888adds a fallback only when no future
vision_startcandidate exists. Thatprevents the last-media exception. However, when a future marker exists, it is
still selected, so the future-media reproduction above continues to associate
the current media metadata with the later media block.
I searched open and closed issues and pull requests using
recompute_mrope_positions,vision_start_indices,chunked prefill mrope,IndexError evs,multimodal pruning, andEVS, and did not find an exactduplicate.
Before submitting a new issue...