From 0fe9ed0d3cc44817eeabea26751e23372ed54aed Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Sun, 27 Sep 2026 21:20:50 +0200 Subject: [PATCH] fix: Anthropic API streams report a failed response as a finished one (#31405) When the provider failed partway through a streaming request to the Anthropic Messages endpoint, the stream still ended like a normally finished answer, so Claude Code and the Anthropic SDKs took the cut-off text as complete. The stream now ends with an Anthropic error event, with the provider's error message if it sent one, so clients raise an error. Successful streams are unchanged. Fixes #31403 --- backend/open_webui/utils/anthropic.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/backend/open_webui/utils/anthropic.py b/backend/open_webui/utils/anthropic.py index 4121ba202a..bd2cea2e61 100644 --- a/backend/open_webui/utils/anthropic.py +++ b/backend/open_webui/utils/anthropic.py @@ -616,6 +616,7 @@ async def openai_stream_to_anthropic_stream(openai_stream_generator, model: str server_tool_use = None service_tier = None stop_reason = 'end_turn' + error_message = None # Track content blocks with a running index. # Each text block or tool_use block gets its own index. @@ -671,6 +672,13 @@ async def openai_stream_to_anthropic_stream(openai_stream_generator, model: str except (JSONCodec.JSONDecodeError, TypeError): continue + error = data.get('error') + if error: + error_message = ( + error.get('message') if isinstance(error, dict) else error + ) or 'Chat completion stream failed' + break + usage_data = data.get('usage') if isinstance(usage_data, dict): cache_creation = usage_data.get('cache_creation_input_tokens') @@ -904,8 +912,18 @@ async def openai_stream_to_anthropic_stream(openai_stream_generator, model: str } stop_reason = stop_reason_map.get(finish_reason, 'end_turn') + if error_message: + break + except Exception as e: log.error(f'Error in Anthropic stream conversion: {e}') + error_message = 'Chat completion stream failed' + + # Skip message_stop so a failed stream is not reported as complete. + if error_message: + error_event = {'type': 'error', 'error': {'type': 'api_error', 'message': error_message}} + yield f'event: error\ndata: {JSONCodec.dumps(error_event)}\n\n'.encode() + return # Close any open thinking block if thinking_block_open: