#41611·litellm

[Bug]: Streaming guardrails: value split across two SSE chunks can pass per-chunk checks

Author: ninadphalakCreated Sep 17, 2026Updated Sep 17, 2026
Labelsbugproxyllm translation

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

When streaming a chat completion through the proxy with guardrails enabled, sensitive values that are split across consecutive Server-Sent Event (SSE) chunks can pass inspection unmasked because each chunk is evaluated in isolation.

Expected: An inquiry into whether guardrail evaluation can buffer or retain state across chunk boundaries so fragmented entities are detected, or if per-chunk inspection is the intended design for streaming throughput.

User Flow

Before a (hypothetical) fix: The flow fails when a protected string is split across consecutive streaming chunks and arrives unmasked at the client.

  1. The client sends a POST request to http://127.0.0.1:4000/v1/chat/completions with "stream": true requesting text that contains a sensitive value (for example, an email address).
  2. The proxy responds with HTTP 200 and Content-Type: text/event-stream.
  3. The client receives Chunk 1: data: {"choices":[{"delta":{"content":"user@exa"}}]}.
  4. The client receives Chunk 2: data: {"choices":[{"delta":{"content":"mple.com"}}]}.
  5. The client concatenates the chunks in the browser or terminal, reassembling "[email protected]". Security consequence: The recipient observes the unmasked sensitive string because neither isolated chunk matched the redaction pattern on the wire.

After a (hypothetical) fix: The flow succeeds because boundary tokens are retained across chunks so the reassembled entity is masked before reaching the client.

  1. The client sends a POST request to http://127.0.0.1:4000/v1/chat/completions with "stream": true requesting text that contains a sensitive value (for example, an email address).
  2. The proxy responds with HTTP 200 and Content-Type: text/event-stream.
  3. The client receives Chunk 1: the proxy buffers trailing characters or yields only non-sensitive tokens.
  4. The client receives Chunk 2: the proxy evaluates the cross-chunk boundary and replaces the reassembled entity with a redaction marker (e.g. [EMAIL]).
  5. The client concatenates the chunks in the browser or terminal, receiving [EMAIL]. Security consequence: The recipient can no longer observe the unmasked sensitive string in the stream.

Proof the bug occurs

Context / Framing

This is an architectural inquiry and edge-case test regarding cross-chunk state accumulation in streaming guardrail hooks, rather than a crash report.

The Mechanism

When an upstream provider streams tokens that fragment a sensitive value across two consecutive Server-Sent Events (SSE), per-chunk guardrails can miss the pattern:

  • Event 1: data: {"choices":[{"delta":{"content":"user@exa"}}]}
  • Event 2: data: {"choices":[{"delta":{"content":"mple.com"}}]}

Neither individual chunk triggers an email pattern match, but the client reassembles "[email protected]".

Configuration

model_list:
  - model_name: gpt-4o-mini
    litellm_params:
      model: gpt-4o-mini
litellm_settings:
  callbacks: ["presidio"]

Reproduction Harness

Because reproducing token fragmentation deterministically against live LLM providers is expensive and non-deterministic, I built a zero-credential, endpoint-neutral CI harness (pii-leak-benchmark) that injects deterministic split-chunk boundaries directly into the streaming proxy:

- uses: ninadphalak/[email protected]
  with:
    target-base-url: http://127.0.0.1:4000/v1
    start-command: ./scripts/start-litellm.sh
    upstream-env: OPENAI_API_BASE

Questions for Maintainers

  1. Does LiteLLM's streaming guardrail hook currently retain a sliding window / accumulated text across chunks, or evaluate each chunk independently?
  2. Would an automated CI regression check for split-chunk evasion be useful to the project? Happy to submit a PR.

Harness reference: https://pypi.org/project/pii-leak-benchmark/

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on ?

1.99.0

Twitter / LinkedIn details

https://www.linkedin.com/in/ninadphalak/