From 0a50954090a396f6b2e4f57f4105ff912a73561d Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Sat, 10 Oct 2026 18:16:10 +0200 Subject: [PATCH] fix: Chat failed notifications are not sent when the provider returns an error status (#32198) Notification targets subscribed to Chat failed stayed silent when the provider answered a chat with an error status, such as a 500, or could not be reached at all. Those failures show up as an error in the chat, but only a few uncommon failures were announced to the targets. Now these failures notify Chat failed targets too, with the error text and a link to the chat. Fixes #32003 --- backend/open_webui/main.py | 6 ++++ backend/open_webui/utils/middleware.py | 44 +++++++++++++++----------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 6cea135478..feab206fa6 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -253,6 +253,7 @@ from open_webui.utils.middleware import ( build_chat_response_context, process_chat_payload, process_chat_response, + publish_chat_failed_event, ) from open_webui.utils.misc import get_response_error_detail, merge_model_params from open_webui.utils.model_ids import strip_provider_model_prefix @@ -1707,6 +1708,11 @@ async def chat_completion( ) except Exception: log.exception('Failed to emit chat error') + + try: + await publish_chat_failed_event(request, user, metadata, str(error_detail)) + except Exception: + log.exception('Failed to publish chat failed event') finally: # Clean up MCP clients. Each client is isolated so one # failure doesn't skip the rest. diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index abdfa62f09..9c0336a75b 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -237,6 +237,30 @@ async def publish_chat_finished_event( await event_emitter({'type': 'chat:list', 'data': {'chat_id': chat_id, 'folder_id': folder_id}}) +async def publish_chat_failed_event(request: Request, user: UserModel, metadata: dict, error: str): + chat_id = metadata.get('chat_id') + if getattr(request.state, 'internal', False) is True or not is_saved_chat_id(chat_id): + return + + webui_url = await Config.get('webui.url') + await publish_event( + request, + EVENTS.CHAT_FAILED, + actor=user, + subject_id=chat_id, + subject_type='chat', + data={ + 'user_id': user.id, + 'chat_id': chat_id, + 'message_id': metadata.get('message_id'), + 'model_id': metadata.get('model_id'), + 'url': f'{webui_url}/c/{chat_id}' if webui_url else f'/c/{chat_id}', + 'message': error, + }, + message='Chat failed', + ) + + # We believe in one maker of all models, seen and unseen, # and in the reasoning which proceeds from the architect. # We look for the resurrection of dead processes and the @@ -4361,25 +4385,7 @@ async def non_streaming_chat_response_handler(response, ctx): response = build_response_object(response, merge_events_into_response(response_data, events)) except Exception as e: log.debug('Error occurred while processing request: %s', e) - chat_id = metadata.get('chat_id') - if getattr(request.state, 'internal', False) is not True and chat_id and is_saved_chat_id(chat_id): - webui_url = await Config.get('webui.url') - await publish_event( - request, - EVENTS.CHAT_FAILED, - actor=user, - subject_id=chat_id, - subject_type='chat', - data={ - 'user_id': user.id, - 'chat_id': chat_id, - 'message_id': metadata.get('message_id'), - 'model_id': metadata.get('model_id'), - 'url': f'{webui_url}/c/{chat_id}' if webui_url else f'/c/{chat_id}', - 'message': str(e), - }, - message='Chat failed', - ) + await publish_chat_failed_event(request, user, metadata, str(e)) pass return response