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
This commit is contained in:
Classic298 2026-09-27 21:20:50 +02:00 • committed by GitHub
parent 332237662f
commit 0fe9ed0d3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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: