The Model Did Nothing Wrong
A hosted model returned other customers' data in 155 responses. The cause was a serving configuration, not the model, and nothing in the status code or response format signaled it.
Between August 27 and September 9, a model hosted on Cloudflare Workers AI sometimes answered a prompt with fragments of someone else's request. In total, 155 responses across a small number of customers contained data from other customers' requests sent shortly before.
The model behaved correctly the entire time. That is the part I keep thinking about.
Cloudflare published a detailed postmortem on October 26, coordinated with an advisory from the maintainers of SGLang, the open-source inference engine involved. It is one of the clearest accounts I have read of how an inference optimization turns into a data exposure. Anyone who sends sensitive data to a hosted model should read it.
What happened
GLM-5.3 was offered with a context window of 1,048,576 tokens. The GPU memory reserved for prompt state during prefill, the KV cache pool, held 681,280 tokens. The team knew about the gap before launch. They expected the engine to reject prompts that did not fit, and their pre-launch tests returned successful status codes with no errors, so the gap looked harmless.
It was not. The model ran with SGLang in disaggregated mode, where one set of GPUs processes the prompt (prefill), another generates the response (decode), and the cache is handed between them. In that mode, a prompt too long for the pool was not aborted. It was replaced with a one-token placeholder and allowed to continue. The decode side then read cache pages that had never been filled for that request. Those pages still held earlier requests. The caller received HTTP 200 and text built partly from another customer's prompt.
Every affected prompt was longer than 681,280 tokens. Security researcher Gadi Evron of Knostic reported the issue through Cloudflare's bug bounty program on September 8. The next day, after declaring an incident, Cloudflare reproduced the leak within 25 minutes, took the model offline within 40 minutes of the declaration, and restored it that evening with a much larger cache pool and an engine patch that rejects oversized requests before any handoff. Cloudflare found no evidence of deliberate exploitation.
Three lessons
1. A 200 is not a pass. Cloudflare names its own testing gap plainly: it validated status codes and performance, not the content or coherence of the output. Those long-context tests passed without errors. In an AI system the response body is the product. Tests that never look at what came back cannot catch this class of bug.
2. Fail closed at capacity limits. The engine had a rejection path. The disaggregated path degraded instead: it substituted a placeholder and kept going. Silent degradation at a resource boundary is a security bug, not a performance quirk. When input exceeds what a system can safely hold, the only acceptable outcome is an error the caller can see. Cloudflare now measures pool size against configured context, from the running engine, before any disaggregated deployment change ships. That is the right instinct: verify the invariant directly instead of trusting a component to enforce it.
3. Tenant isolation lives in configuration you cannot see. From the API, a leaked response looked like any other: HTTP 200, coherent text. Isolation depended on cache sizing, a transfer protocol, and an admission check deep inside the provider's serving stack. That is not a reason to avoid hosted inference. It is a reason to ask better questions of the providers you rely on.
What I would add to an AI vendor review
- How are KV and prefix caches scoped: per request, per tenant, or shared?
- Is configured context length checked against cache capacity before a deployment goes live?
- Do pre-release tests inspect output content, or only status codes and latency?
- When a request exceeds a limit, does the system return an error, or does it truncate, substitute, or continue?
- How and how quickly will you notify customers if their data appears in another tenant's response?
If you run SGLang yourself with prefill/decode disaggregation, Cloudflare recommends v0.5.22 or later, and the SGLang advisory lists further transfer-backend fixes through v0.5.23. Cloudflare recommends measuring KV pool capacity against configured context on every node after any relevant change, and SGLang describes its fix as a complement to that check, not a replacement.
Why this matters more in healthcare
In a regulated setting, a leak like this is an incident even though nobody on your side made a mistake. Your code handled the prompt correctly, the API accepted it, and a configuration you never saw exposed it. Contracts and business associate agreements matter, but they do not tell you how a provider partitions its cache. You have to ask.
Credit where it is due. Cloudflare and the SGLang maintainers published root causes, timelines, configuration values, and exact fixes, including the parts that reflect poorly on their own testing. That candor is how the rest of us learn, and I would like to see more inference providers do the same.