Check for existing issues
What happened?
Description
When a Bedrock guardrail hard-blocks a streamed response,
async_post_call_streaming_iterator_hook raises an HTTPException inside
the async generator body. By the time the guardrail runs, the HTTP 200
header has already been written to the client — the exception cannot be
converted to a 4xx response. Instead it propagates up, truncating the SSE
stream mid-flight. The client receives a partial or malformed response with
no indication of why.
Expected behaviour
The client receives a well-formed SSE error frame followed by [DONE]:
data: {"error": {"message": "Violated guardrail policy", "code": 400, "type": "guardrail_violation"}}
data: [DONE]
Actual behaviour
The HTTPException escapes the generator. The stream is truncated and the
client has no structured error to parse.
Root cause
async_post_call_streaming_iterator_hook has no try/except around the
make_bedrock_api_request / asyncio.gather calls. Both the parallel
INPUT+OUTPUT path (should_validate_input=True) and the output-only path
(should_validate_input=False) are affected.
GuardrailInterventionNormalStringError (soft-block /
disable_exception_on_block=True) is not affected — it is already
caught by inner try/except blocks.
Fix
Wrap the Bedrock validation block in a try/except HTTPException and yield
two in-band SSE frames instead of letting the exception escape.
Steps to Reproduce
- Configure a BedrockGuardrail with
event_hook: post_call (or during_call).
- Set
disable_exception_on_block to false (the default).
- Send a streaming request whose output triggers a hard-block policy
(e.g. topic policy, content policy — anything that returns action: BLOCKED).
- Observe the SSE stream terminates abruptly without an error frame.
Relevant log output
What part of LiteLLM is this about?
Proxy
What LiteLLM version are you on ?
v1.83.7
Twitter / LinkedIn details
No response
Check for existing issues
What happened?
Description
When a Bedrock guardrail hard-blocks a streamed response,
async_post_call_streaming_iterator_hookraises anHTTPExceptioninsidethe async generator body. By the time the guardrail runs, the HTTP 200
header has already been written to the client — the exception cannot be
converted to a 4xx response. Instead it propagates up, truncating the SSE
stream mid-flight. The client receives a partial or malformed response with
no indication of why.
Expected behaviour
The client receives a well-formed SSE error frame followed by
[DONE]:data: {"error": {"message": "Violated guardrail policy", "code": 400, "type": "guardrail_violation"}}
data: [DONE]
Actual behaviour
The
HTTPExceptionescapes the generator. The stream is truncated and theclient has no structured error to parse.
Root cause
async_post_call_streaming_iterator_hookhas notry/exceptaround themake_bedrock_api_request/asyncio.gathercalls. Both the parallelINPUT+OUTPUT path (
should_validate_input=True) and the output-only path(
should_validate_input=False) are affected.GuardrailInterventionNormalStringError(soft-block /disable_exception_on_block=True) is not affected — it is alreadycaught by inner
try/exceptblocks.Fix
Wrap the Bedrock validation block in a
try/except HTTPExceptionand yieldtwo in-band SSE frames instead of letting the exception escape.
Steps to Reproduce
event_hook: post_call(orduring_call).disable_exception_on_blocktofalse(the default).(e.g. topic policy, content policy — anything that returns
action: BLOCKED).Relevant log output
What part of LiteLLM is this about?
Proxy
What LiteLLM version are you on ?
v1.83.7
Twitter / LinkedIn details
No response