diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index f19bffc019..d040371b1f 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -241,7 +241,7 @@ from open_webui.utils.middleware import ( process_chat_payload, process_chat_response, ) -from open_webui.utils.misc import merge_model_params +from open_webui.utils.misc import get_response_error_detail, merge_model_params from open_webui.utils.model_ids import strip_provider_model_prefix from open_webui.utils.models import ( check_model_access, @@ -1638,14 +1638,7 @@ async def chat_completion( # raise so the except-block below emits chat:message:error + # chat:tasks:cancel, unblocking the frontend. if isinstance(response, JSONResponse) and response.status_code >= 400: - try: - error_body = JSONCodec.loads(response.body.decode('utf-8', 'replace')) - detail = error_body.get('error', error_body) if isinstance(error_body, dict) else error_body - if isinstance(detail, dict): - detail = detail.get('message', detail.get('detail', str(detail))) - except Exception: - detail = f'Provider returned HTTP {response.status_code}' - raise Exception(detail) + raise Exception(get_response_error_detail(response)) ctx = await build_chat_response_context(request, form_data, user, model, metadata, tasks, events) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 4d5eeebcd7..092c2dd3b8 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -111,6 +111,7 @@ from open_webui.utils.misc import ( get_last_user_message_item, get_message_list, get_output_text, + get_response_error_detail, get_reasoning_details, get_system_message, is_string_allowed, @@ -5958,6 +5959,9 @@ async def streaming_chat_response_handler(response, ctx): await stream_body_handler(res, new_form_data) output[:0] = prior_output prior_output = [] + elif getattr(res, 'status_code', 200) >= 400: + await emit_message_error(get_message_error_content(get_response_error_detail(res))) + break else: break except Exception as e: @@ -6142,6 +6146,9 @@ async def streaming_chat_response_handler(response, ctx): if isinstance(res, StreamingResponse): await stream_body_handler(res, new_form_data) + elif getattr(res, 'status_code', 200) >= 400: + await emit_message_error(get_message_error_content(get_response_error_detail(res))) + break else: break except Exception as e: diff --git a/backend/open_webui/utils/misc.py b/backend/open_webui/utils/misc.py index 2761adc191..beb91d2b04 100644 --- a/backend/open_webui/utils/misc.py +++ b/backend/open_webui/utils/misc.py @@ -40,6 +40,31 @@ def merge_model_params(base: dict, override: dict) -> dict: return params +def get_response_error_detail(response: object) -> str: + status_code = getattr(response, 'status_code', None) + fallback = f'Provider returned HTTP {status_code}' if status_code else 'Provider returned an error' + + try: + body = response.body + if not isinstance(body, str): + body = body.decode('utf-8', 'replace') + detail = JSONCodec.loads(body) + except Exception: + return fallback + + while isinstance(detail, dict): + next_detail = None + for key in ('error', 'message', 'detail'): + if key in detail: + next_detail = detail[key] + break + if next_detail is None: + return str(detail) + detail = next_detail + + return detail if isinstance(detail, str) else str(detail) + + def _strip_filter_entry(entry): # Compose list-form env syntax passes surrounding quotes through verbatim return (entry or '').strip().strip('"\'').strip()