Cloudflare published a postmortem on October 26 describing how 155 responses from its GLM-5.3 Workers AI model leaked data from other customers’ sessions. The model, served as @cf/zai-org/glm-5.3, was exposed from its August 27, 2026 launch until September 9, when Cloudflare took it offline and restored it the same day. The leak only hit prompts longer than 681,280 tokens, and Cloudflare says it found no evidence of deliberate exploitation after excluding its own testing and the researcher’s. Gadi Evron of Knostic reported the bug through HackerOne on September 8.

The interesting part is not that a model leaked data. It is how the leak happened, because the mechanism is a live hazard for anyone running disaggregated inference, not just Cloudflare.

A pool that was too small, and an engine that lied about it

Cloudflare deployed GLM-5.3 with a 1,048,576-token context window. The KV cache pool on its prefill GPUs held only 681,280 tokens. Cloudflare knew about the mismatch before launch. It expected SGLang, the open-source inference engine underneath, to return an error for any prompt it could not fit. Its long-context tests returned successful HTTP status codes, so the team assumed the engine was handling the gap gracefully.

It was not. In SGLang v0.5.21 and earlier, a prompt longer than the pool got marked as rejected and swapped for a one-token placeholder. On a single-node deployment that path worked: the placeholder ran, the batch finished, and the client got an error. Cloudflare runs GLM-5.3 in disaggregated mode, with a pool of prefill GPUs handing KV cache over RDMA (via Mooncake) to a pool of decode GPUs. In that setup, the admission queues on both prefill and decode checked whether a request was finished(), not whether it was rejected. A rejected request stored its rejection in to_finish, not finished_reason, so finished() still returned false. The request got admitted anyway.

Then the decode node reserved pages for the full prompt, prefill wrote only one page of KV for the placeholder, and decode’s attention read all the reserved pages. Pages P1 through P7 still held whatever earlier requests had left in them. The client got HTTP 200, a coherent reply, and other users’ prompt data. It also arrived faster than a real 8-page prefill should take.

That last detail is the one that should worry operators. The bug did not produce an error, a timeout, or a garbled response. It produced a normal-looking answer built partly on someone else’s tokens.

The testing gap is the real story

Cloudflare is unusually candid about its own failure here. It validated HTTP response codes and engine performance at long contexts. It did not validate model output and coherence. That is the gap. A 200 status code told the team the request worked; nothing told them the answer was built on stale cache pages.

The company now checks KV pool size against context length directly from the running engine before any disaggregated deployment ships. It also patched the engine so a request carrying a pending rejection is retired at the disaggregation admission queue, with its original error, before any bootstrap, allocation, or KV transfer happens. That patch went upstream as SGLang PR #38704, and the decode node now verifies that the number of tokens transferred matches the number it reserved. SGLang merged a related fix in #36988 on September 6 and a follow-up in #38935. Cloudflare recommends every operator move to SGLang v0.5.22 or later.

There is a detail in the timeline that deserves more attention than the postmortem gives it. The same bug had been reported to SGLang by another user as issue #30233 on July 6, 2026, and Cloudflare only noticed it while investigating this incident. Cloudflare says it had been flagging these types of issues with SGLang since v0.5.10. So the rejection path was known to be fragile for months, and a public issue sat open for two months before a production tenant leak forced the fix.

What disaggregated inference gets wrong about memory

The KV cache is a shared pool of fixed-size pages in GPU memory. Each request gets pages when it is admitted and releases them when it finishes. Pages are not zeroed between requests. The next request overwrites them. Cloudflare notes this is common across inference engines and necessary for performance. It is also why the engine is responsible for tracking page ownership and lifetime, and that tracking is genuinely hard. Ownership is shared within a node (KV offload and token generation touch the same memory with different lifetimes) and technically shared with the remote prefill node, which holds a simultaneous mutable reference to the page.

SGLang sizes the pool at startup from whatever GPU memory remains after loading model weights. The model’s advertised context length is a separate configuration value. Nothing forces the two to agree. Cloudflare’s post says plainly that checking the two match is the operator’s job. That is a sharp admission: the engine will happily run with a pool smaller than the context window, and the failure mode is silent.

The bug did not produce an error, a timeout, or a garbled response. It produced a normal-looking answer built partly on someone else’s tokens.

The economics of long context just got a security line item

Long-context windows are a selling point right now. A 1,048,576-token window is the kind of number that goes in a launch post. But the pool that actually holds the KV cache is sized by leftover GPU memory, not by the advertised window, and the gap between the two is where this bug lived. Any operator running disaggregated prefill/decode on SGLang, vLLM, or a similar engine with a shared page pool has the same structural question: does your pool hold your context window, on both prefill and decode, for every model you serve? Cloudflare says every other disaggregated deployment on Workers AI has pools larger than context length on both sides, which is why GLM-5.3 was the only model affected. That is a property of Cloudflare’s config, not a guarantee the engine enforces.

For builders, the practical takeaway is narrow and unglamorous. Pin your inference engine to a patched version. Check pool size against context length directly, not by watching for 200s. And if you are running multi-tenant inference, test whether a request that exceeds the pool returns an error or quietly returns something else. Cloudflare’s 155 affected responses came from ordinary long-context use, not an attacker. The next operator to hit this may not get a Knostic researcher filing a HackerOne report within twelve days.

The open question is upstream. Cloudflare’s fix is in SGLang v0.5.22, but the July 6 issue sat for two months, and Cloudflare says it had been raising these problems since v0.5.10. Whether the SGLang maintainers now treat pool-versus-context validation as a startup check rather than an operator responsibility is the thing to watch.