Skip to content

GLM-5.2 vLLM Block-Table Errors: Fixed Versions

Independent research — not an official Z.ai publication.Identity and provider disclosure

A cool-blue diagnostic flow compares a narrow block table with a one-column amber extension labeled alignment and a second pair whose wider table is exactly twice the narrow table labeled DCP2, then routes them to vLLM 0.27 and 0.28 release checkpoints

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:

The shared surface symptom
RuntimeError: The expanded size of the tensor (...) must match
the existing size (...) at non-singleton dimension 1

For 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.

  1. Classify the three width signatures
  2. Reproduce the one-column alignment math
  3. Verify the alignment fix in v0.27.0
  4. Reproduce the DCP-factor mismatch
  5. Verify why DCP waits for v0.28.0
  6. Choose a stable version from the matrix
  7. Inspect the installed source without a GPU
  8. Use the ratio as a diagnostic, not a verdict
  9. Run a one-variable upgrade canary
  10. Test MTP and prefix caching separately
  11. Handle an old-version workaround carefully
  12. Separate nearby GLM-5.2 failures
  13. Rent a test cluster only after preflight
  14. Frequently asked questions
  15. Sources and method

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.

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:

128-token block-table alignment
block_alignment = 128 / gcd(128, 64) = 2
raw_blocks = ceil(max_model_len / 64)
aligned_blocks = ceil(raw_blocks / 2) × 2

If 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.

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:

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.

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:

DCP group versus per-layer width before the fix
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,576
block_size = 64
dcp_size = 2
group_width = 1,048,576 / 64 = 16,384
per_layer_width = 1,048,576 / (64 × 2) = 8,192

The 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.

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.

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:

Audit the installed package without initializing CUDA
from importlib.metadata import distribution
from 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:

Pin a source checkout
git -C /path/to/vllm rev-parse HEAD
git -C /path/to/vllm status --short

A 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:

Route a logged width pair to the next source check
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 #50302
DCP candidate: verify group/layer specs and PR #50823
unmatched: compare widths with old/new max_model_len and audit source

The 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.

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.”

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.

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.

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.

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.

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.

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.

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.

Primary sources checked on August 27, 2026 HKT:

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.