GLM-5.2 vLLM Block-Table Errors: Fixed Versions
Independent research — not an official Z.ai publication.Identity and provider disclosure
Original editorial diagnostic. It visualizes width relationships; it is not a vLLM screenshot, CUDA trace, benchmark result, or proof of a fixed cluster.
The traceback looks like a generic PyTorch shape failure:
RuntimeError: The expanded size of the tensor (...) must matchthe existing size (...) at non-singleton dimension 1For GLM-5.2’s DSA sparse-indexer path, the two numbers are more useful than the word “expanded.” They reveal which code paths calculated different block-table widths. Treating every pair as an off-by-one can hide a DCP configuration bug; treating every pair as DCP can hide a stale table created before an automatic context reduction.
This guide audits the public GLM-5.2-FP8 checkpoint at
ba978f7d…,
vLLM issue
#46074, merged alignment
fix #50302, DCP issue
#50825, and merged DCP fix
#50823. The
machine-readable receipt
hashes 22 public artifacts and regenerates the width tables. We made zero
model calls, zero local GPU runs, and zero local upstream-test runs.
In this guide
Section titled “In this guide”- Classify the three width signatures
- Reproduce the one-column alignment math
- Verify the alignment fix in v0.27.0
- Reproduce the DCP-factor mismatch
- Verify why DCP waits for v0.28.0
- Choose a stable version from the matrix
- Inspect the installed source without a GPU
- Use the ratio as a diagnostic, not a verdict
- Run a one-variable upgrade canary
- Test MTP and prefix caching separately
- Handle an old-version workaround carefully
- Separate nearby GLM-5.2 failures
- Rent a test cluster only after preflight
- Frequently asked questions
- Sources and method
Classify the three width signatures
Section titled “Classify the three width signatures”Start with the two dimension-1 widths, the resolved KV cache type, selected
attention backend, DCP size, and whether vLLM changed max_model_len during
startup. Do not begin by adding an arbitrary column to a tensor.
| Logged relationship | Likely calculation disagreement | Upstream boundary | First stable release |
|---|---|---|---|
Difference is exactly 1: 1563/1564, 3125/3126, 5469/5470 |
One side applies 128-token alignment to 64-token blocks; the indexer side does not | PR #50302 | v0.27.0 |
Larger width is exactly dcp_size × smaller width: 8192/16384 under DCP2 |
Uniform group spec is unsharded while its MLA layer spec is DCP-sharded | PR #50823 | v0.28.0 |
Widths map to old and reduced context limits: 2464/3168 |
InputBatch retains the original width after max_model_len=auto changes |
Open PR #46794 at this audit | No stable boundary established here |
| No relationship matches | Different runner, block size, kernel split, MTP overhang, or another defect | Source-audit the actual build | Do not guess |
The table is a routing aid, not a stack-free classifier. Confirm the failure
site includes the MLA/DSA indexer and a copy into
expanded_block_table_buffer. A failure in weight loading, KV allocation,
FlashMLA workspace allocation, MoE kernels, graph capture, or NCCL belongs to a
different investigation.
The upstream reports use fp8_ds_mla, FLASHMLA_SPARSE, concurrent decode,
and MTP in several profiles. Those details make the bug reachable; they do not
mean every FP8, MTP, or long-context failure has the same root cause.
Reproduce the one-column alignment math
Section titled “Reproduce the one-column alignment math”The alignment case is deterministic. GLM-5.2’s reported sparse path uses 64-token KV blocks. Some attention backends require the block table to cover a 128-token quantum. vLLM therefore needs an even number of 64-token columns:
block_alignment = 128 / gcd(128, 64) = 2
raw_blocks = ceil(max_model_len / 64)aligned_blocks = ceil(raw_blocks / 2) × 2If raw_blocks is odd, alignment adds one column. If it is even, both widths
already match.
max_model_len |
Raw width | Aligned width | Delta | Upstream result |
|---|---|---|---|---|
| 100,000 | 1,563 | 1,564 | +1 | Earlier GLM sparse-indexer report |
| 106,816 | 1,669 | 1,670 | +1 | PR regression case |
| 200,000 | 3,125 | 3,126 | +1 | H200 reproduction comment |
| 230,000 | 3,594 | 3,594 | 0 | Stable in issue #46074 |
| 300,000 | 4,688 | 4,688 | 0 | Stable in issue #46074 |
| 325,000 | 5,079 | 5,080 | +1 | Crash in issue #46074 |
| 350,000 | 5,469 | 5,470 | +1 | Primary issue crash |
The calculation reproduces both the exact 5469 vs 5470 exception and the
reported stable/crash parity at 230K, 300K, 325K, and 350K. That is strong
source-level evidence for the width diagnosis. It does not reproduce the
reporter’s 4-node, 32-H100 service or its request success rate.
Issue #46074 also records a separate H200 observation at 200,000 tokens with
MTP and heavy prefix-cache hits. The logged pair was 3125 vs 3126. The same
parity explains the width, but the trigger shows why “it only fails above
325K” is too broad. Workload state can bring the edge into an otherwise lower
configured limit.
Verify the alignment fix in v0.27.0
Section titled “Verify the alignment fix in v0.27.0”Before the fix, MultiGroupBlockTable rounded its width while the sparse
indexer allocated expanded_block_table_buffer from an unaligned cdiv.
Both objects described the same request but reserved different column counts.
Merged PR #50302 moved the
calculation into one get_block_table_width helper. The model runner, block
table, metadata builder, and indexer then consume the same aligned width. Its
CPU regression includes get_block_table_width(1875, 64) == 1876 and also
checks virtual kernel-block splitting.
The source boundary is verifiable rather than inferred from release dates. A release date alone is not commit containment:
- v0.26.0
block_table.pycontains the old local alignment and no shared helper. - v0.27.0
block_table.pydefinesget_block_table_width. - v0.27.0
indexer.pyrequires the calculatedblock_table_widthrather than recomputing it. - The v0.27.0 tag
descends from merge commit
a0cd2b69….
Therefore v0.27.0 is the first stable release containing the alignment fix. This statement is limited to that defect. It does not make v0.27.0 the answer for the DCP-factor signature.
Reproduce the DCP-factor mismatch
Section titled “Reproduce the DCP-factor mismatch”The DCP failure predates the alignment fix but appears through the same tensor copy. One side reads a uniform cache-group spec; the other reads its per-layer MLA spec. Before PR #50823, their width formulas differed:
group_width = ceil(max_model_len / block_size)per_layer_width = ceil(max_model_len / (block_size × dcp_size))For issue #50825’s GLM-5.2 DCP2 profile:
max_model_len = 1,048,576block_size = 64dcp_size = 2
group_width = 1,048,576 / 64 = 16,384per_layer_width = 1,048,576 / (64 × 2) = 8,192The larger width is exactly twice the smaller width because DCP size is two. This is not 128-token parity: both numbers are already even. Adding a single column cannot reconcile them.
PR #50823 makes
UniformTypeKVCacheSpecs.max_num_blocks_per_req delegate to its constituent
layer specs and asserts they agree. Its CPU test reduces the same relationship
to 1,024 tokens, 16-token blocks, and DCP2: the old group reports 64 while the
layer reports 32; after the fix both report 32.
Verify why DCP waits for v0.28.0
Section titled “Verify why DCP waits for v0.28.0”PR #50823 merged on August 3, before v0.27.0 was publicly released on August 10. That chronology is tempting but insufficient. A release branch can be cut before a main-branch merge.
The tagged files settle the question:
| Tag | UniformTypeKVCacheSpecs DCP-aware width override |
File receipt |
|---|---|---|
| v0.27.0 | Absent | bfbd62f0… |
| v0.27.1 | Absent | bfbd62f0…—byte-identical for the audited file |
| v0.28.0 | Present | 932e9175… |
The v0.28.0 tag
also descends from merge commit f0de1a60…. It was released on August 26.
Thus v0.28.0 is the first stable release containing the DCP group-width fix
and the first audited stable tag containing both fixes in this guide.
Do not read this as “v0.27.1 is generally broken for DCP” or “v0.28.0 makes every GLM-5.2 DCP topology correct.” The claim is narrower: the audited classic sparse-indexer group/per-layer width override is absent in the former and present in the latter. Backend support, decode LSE, kernel selection, output correctness, and memory capacity still need their own gates.
Choose a stable version from the matrix
Section titled “Choose a stable version from the matrix”| Stable tag | Alignment helper shared with indexer | DCP group width delegated | Operational decision |
|---|---|---|---|
| v0.26.0 | No | No | Treat both audited signatures as possible on the affected classic path. |
| v0.27.0 | Yes | No | First stable answer only for the one-column alignment signature. |
| v0.27.1 | Yes | No | Same audited boundary; do not use it as the DCP-factor fix. |
| v0.28.0 | Yes | Yes | First stable candidate containing both fixes; still run the canary. |
If the installed image is based on a development build, vendor fork, backport, or cherry-pick, the marketing tag is secondary. Resolve the exact files or commit. A v0.26-labelled image can contain a backport; a v0.28-labelled image can vendor an older source tree. Source wins.
For a clean upstream installation with either audited signature, start the upgrade evaluation at v0.28.0 or a later pinned stable release. That avoids needing two different recommendations. The earlier v0.27.0 boundary remains useful for forensics and tightly constrained fleets that cannot yet change their larger runtime base.
Inspect the installed source without a GPU
Section titled “Inspect the installed source without a GPU”Do not import the vLLM runtime merely to print a version on a production host. Python package metadata and installed text files are enough for this source preflight:
from importlib.metadata import distributionfrom pathlib import Path
dist = distribution("vllm")root = Path(dist.locate_file(""))block_table = (root / "vllm/v1/worker/block_table.py").read_text()kv_interface = (root / "vllm/v1/kv_cache_interface.py").read_text()
uniform = kv_interface.split("class UniformTypeKVCacheSpecs", 1)[1][:6000]
print("version:", dist.version)print("shared_alignment_width:", "def get_block_table_width" in block_table)print( "dcp_group_width_override:", "def max_num_blocks_per_req" in uniform and "self.kv_cache_specs.values()" in uniform,)Also hash those files and record the container image digest. The two booleans are evidence for these specific code features, not a general compatibility test. If a vendor renamed classes, split the module, or patched equivalent logic elsewhere, inspect the diff rather than forcing the script to say yes.
For a source checkout, record both the commit and worktree state:
git -C /path/to/vllm rev-parse HEADgit -C /path/to/vllm status --shortA dirty checkout or unpinned container invalidates a tag-only diagnosis.
Use the ratio as a diagnostic, not a verdict
Section titled “Use the ratio as a diagnostic, not a verdict”A tiny offline classifier can prevent the most obvious wrong turn:
def classify_widths(left: int, right: int, dcp_size: int) -> str: low, high = sorted((left, right)) if high - low == 1: return "alignment candidate: verify 64-token blocks and PR #50302" if dcp_size > 1 and high == low * dcp_size: return "DCP candidate: verify group/layer specs and PR #50823" return "unmatched: compare widths with old/new max_model_len and audit source"
print(classify_widths(5469, 5470, 1))print(classify_widths(8192, 16384, 2))print(classify_widths(2464, 3168, 1))Expected output:
alignment candidate: verify 64-token blocks and PR #50302DCP candidate: verify group/layer specs and PR #50823unmatched: compare widths with old/new max_model_len and audit sourceThe word candidate matters. Kernel virtual block splitting, non-64 block sizes, DCP rounding, speculative overhang, prefix-cache state, and a different model runner can produce other relationships. The traceback and source path must agree with the arithmetic.
Minimal evidence bundle for an upstream report
Include the exact vLLM version and commit, image digest, model revision,
PyTorch/CUDA, GPU and topology, attention backend, resolved KV dtype, block
size, TP/DCP/DP/EP, model runner, max_model_len requested and effective,
MTP depth, prefix-cache state, complete dimension pair, the first relevant
traceback frames, concurrency, input/output lengths, and whether the error
reproduces after a clean restart. Remove credentials, prompt content, hostnames,
internal IPs, and unrelated environment variables.
Run a one-variable upgrade canary
Section titled “Run a one-variable upgrade canary”First reproduce the old failure in a non-production environment with a pinned, sanitized workload. Then change only the vLLM image or source pin. Do not simultaneously change context, cache dtype, model revision, MTP, DCP, driver, and request corpus; a passing run would have no attributable cause.
| Gate | What to hold constant | What must pass after the upgrade |
|---|---|---|
| Source | Checkpoint, topology, flags, request corpus | Installed files contain the expected matching fix |
| Width boundary | Old failing odd/even lengths or DCP ratio | No tensor-width exception across repeated runs |
| Correctness | Frozen prompts and decode settings | Token/output contract meets the baseline tolerance |
| Service | Concurrency, arrivals, cancellations | No worker death, stuck request, or unexpected restart |
| Memory | Same context and request mix | Physical free HBM and allocator peaks retain a documented reserve |
| Performance | Same warmup and measurement window | TTFT, TPOT, accepted throughput, and tails meet the budget |
| Recovery | Same health checks and orchestration | Intentional restart and rollback restore service predictably |
Capture the exact failing pair before the upgrade and confirm its old code path is exercised after it. “We could not make it fail” is weaker than “the same workload crossed the old boundary repeatedly and the service remained correct.”
Test MTP and prefix caching separately
Section titled “Test MTP and prefix caching separately”Issue comments broaden the reachability conditions. The primary 350K profile
used MTP and disabled prefix caching. A separate H200 profile reproduced
3125 vs 3126 around a 200K limit when long shared prefixes were already in
cache and MTP draft tokens extended the active edge. A DGX Spark report linked
another exact-multiple overhang path.
Use four explicit arms:
| Arm | MTP | Prefix caching | Purpose |
|---|---|---|---|
| Control | Off | Off | Prove plain concurrent decode and the new source pin |
| Speculation | On | Off | Exercise flattened multi-token decode without cache-hit state |
| Cache | Off | On | Exercise near-limit shared-prefix hits without draft overhang |
| Combined | On | On | Recreate the highest-risk interaction after separate arms pass |
Sweep both odd and even raw block counts around the old alignment boundary. For DCP, test the intended size and a lower control at the exact production TP and DCP topology. Record accepted outputs, not only server uptime. A service that avoids the exception but silently corrupts or truncates output has not passed.
Handle an old-version workaround carefully
Section titled “Handle an old-version workaround carefully”One issue commenter suggested choosing a max_model_len aligned to
128 × dcp_size for an old build. For the simple 64-token, DCP1 alignment
calculation, that keeps the raw block count even and avoids the one-column
disagreement.
It is an emergency containment, not the fix:
- It does not reconcile the DCP group/per-layer formula.
- It does not prove MTP cannot extend a request across another block.
- It does not cover prefix-cache boundaries or automatic context reduction.
- It can move the failure rather than remove the inconsistent calculation.
- It leaves the fleet on a known old source path.
Prefer a pinned stable release containing the merged fix. If change control temporarily blocks the upgrade, document the aligned limit, validate all four arms above, preserve the previous limit for rollback, and put an expiry date on the exception.
Do not copy an ad hoc +1 patch from an operator repository into production
without reviewing the exact source base. PR #50302 deliberately centralized
alignment and virtual block splitting because scattered local padding is how
the two sides diverged.
Separate nearby GLM-5.2 failures
Section titled “Separate nearby GLM-5.2 failures”The words “runtime,” “FP8,” and “long context” are not enough to group failures.
| Symptom | Next guide or source | Why it is different |
|---|---|---|
CUDA OOM inside sparse_decode_fwd after a mixed burst |
H200 runtime OOM workspace audit | Hidden kernel workspace exceeds physical headroom; no block-table width copy is required |
| Need to decide whether a pinned H200 backend can enable DCP | Decode context parallelism audit | Backend, LSE, kernel, and correctness eligibility precede this narrow width bug |
2464 vs 3168 after max_model_len=auto reduces context |
Open PR #46794 | InputBatch retains an old-width table; the pair follows two context limits |
| Immediate OOM while loading weights or capturing graphs | Local hardware guide | Capacity, quantization, loading duplication, or graph pools fail before this decode copy |
| Output becomes wrong without a tensor exception | Re-run the pinned correctness harness | Absence of the reported exception does not prove kernel or distributed correctness |
If the first relevant frame is not in the MLA indexer, stop using this version matrix as a diagnosis. Follow the actual allocation or kernel path.
Rent a test cluster only after preflight
Section titled “Rent a test cluster only after preflight”The alignment calculation requires no GPU. Spend on a cluster only when the remaining decision is whether the pinned fixed image survives your real topology and workload. Write down the GPU count and HBM, interconnect, driver, image digest, checkpoint revision, storage and egress, maximum spend, sanitized corpus, pass/fail gates, rollback, and deletion plan before opening capacity.
If a production-shaped multi-GPU test is not justified, keep the old service isolated, use a documented hosted route, or test a smaller checkpoint for the application layer. Hardware spend cannot repair an unclassified traceback.
Frequently asked questions
Section titled “Frequently asked questions”Does vLLM v0.27.0 fix the GLM-5.2 5469 vs 5470 error?
Section titled “Does vLLM v0.27.0 fix the GLM-5.2 5469 vs 5470 error?”It is the first stable release containing PR #50302’s shared 128-token width calculation, which addresses the audited one-column alignment path. Verify that your image actually contains the helper and that the traceback follows the same sparse indexer. Then rerun the failing workload.
Does vLLM v0.27.1 fix 8192 vs 16384 under DCP2?
Section titled “Does vLLM v0.27.1 fix 8192 vs 16384 under DCP2?”Not in the audited tagged source. v0.27.1’s kv_cache_interface.py is
byte-identical to v0.27.0 for this class and lacks the group-width override.
v0.28.0 is the first stable tag containing PR #50823.
Should I upgrade directly to v0.28.0?
Section titled “Should I upgrade directly to v0.28.0?”For either audited signature, v0.28.0 is the first stable candidate containing both fixes. Pin its image or commit and run the one-variable canary. Check the current release state again if you are reading later; newer stable releases can supersede this boundary.
Can I fix the error by lowering max_model_len?
Section titled “Can I fix the error by lowering max_model_len?”A lower or specially aligned value can avoid one old alignment boundary, but it does not make the two calculations consistent and does not cover the DCP or auto-reduction paths. Use it only as a time-bounded containment with load tests and an upgrade plan.
Is MTP the root cause?
Section titled “Is MTP the root cause?”Not by itself. Issue #46074 reproduced the alignment failure with multiple MTP
depths, while the column parity follows max_model_len. MTP and prefix-cache
hits can make the edge reachable, so test them as separate workload arms.
Is fp8_ds_mla always affected?
Section titled “Is fp8_ds_mla always affected?”No. It identifies the relevant packed cache path in the upstream GLM reports, but version, model runner, backend, block size, topology, and workload determine whether a specific inconsistent width calculation is present and reached.
What if my widths differ by more than one but are not a DCP multiple?
Section titled “What if my widths differ by more than one but are not a DCP multiple?”Compare them with the original and effective max_model_len, block size, and
kernel block size. Open PR #46794 gives 2464 vs 3168 as an auto-reduction
lookalike. If no formula matches, archive the source and report the actual path;
do not force it into either fixed issue.
Sources and method
Section titled “Sources and method”Primary sources checked on August 27, 2026 HKT:
- Z.ai GLM-5.2 release and the pinned GLM-5.2-FP8 checkpoint
- vLLM issue #46074, PR #50302, and v0.27.0
- vLLM issue #50825, PR #50823, and v0.28.0
- Open PR #46794 for the auto-reduced-context lookalike
We compared raw tagged source rather than release dates alone. v0.27.0 contains the shared alignment helper and its tag descends from #50302. v0.27.0 and v0.27.1 lack the DCP group override; v0.28.0 contains it and descends from #50823. The deterministic receipt recalculates seven alignment rows and two DCP rows, validates 22 source hashes, and records the zero-runtime boundary.
Public search was used to assess result supply; no paid SerpAPI request was made because the site’s August ceiling was already exceeded. Issue comments and operator reports are attributed observations, not GLM52.ai benchmarks. Versions, branches, images, and fixes can change. Recheck current stable tags and the exact installed source before altering a production deployment.
