From d75a7aaf5453e79b6224d49819c27ea5a6f86d15 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Fri, 4 Sep 2026 23:17:02 +0200 Subject: [PATCH] fix: surface upstream errors on arena models instead of crashing (#29662) Sending a message to an arena model failed with "'JSONResponse' object has no attribute 'body_iterator'" whenever the backing provider answered with an HTTP error, so the real error never reached the user. Non-streaming requests on an arena model, such as title and tag generation, broke the same way with "'JSONResponse' object is not a mapping". The arena wrapper assumed the sub-model call always returns a stream for a streaming request and a dict for everything else, but the OpenAI-compatible router returns a plain response object as soon as the provider answers 4xx or 5xx. Both arms now hand that response straight back, which is exactly what the non-arena path already does, so the existing error handling turns it into the usual error message in the chat. Verified against a matrix of streaming and non-streaming requests with the sub-model returning a stream, a dict, a JSONResponse and a PlainTextResponse: both crashes are gone and the two success paths are unchanged, including the selected_model_id prelude on the stream. Fixes #29658 --- backend/open_webui/utils/chat.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/backend/open_webui/utils/chat.py b/backend/open_webui/utils/chat.py index a769d5f1df..0b64a52165 100644 --- a/backend/open_webui/utils/chat.py +++ b/backend/open_webui/utils/chat.py @@ -269,24 +269,25 @@ async def generate_chat_completion( bypass_filter=True, bypass_system_prompt=bypass_system_prompt, ) + # Upstream errors come back as a response object. + if not isinstance(response, StreamingResponse): + return response return StreamingResponse( stream_wrapper(response.body_iterator), media_type='text/event-stream', background=response.background, ) else: - return { - **( - await generate_chat_completion( - request, - form_data, - user, - bypass_filter=True, - bypass_system_prompt=bypass_system_prompt, - ) - ), - 'selected_model_id': selected_model_id, - } + response = await generate_chat_completion( + request, + form_data, + user, + bypass_filter=True, + bypass_system_prompt=bypass_system_prompt, + ) + if not isinstance(response, dict): + return response + return {**response, 'selected_model_id': selected_model_id} if model.get('pipe'): # Below does not require bypass_filter because this is the only route the uses this function and it is already bypassing the filter