diff --git a/backend/open_webui/internal/config.py b/backend/open_webui/internal/config.py index 46f5b1b67d..7b18c362da 100644 --- a/backend/open_webui/internal/config.py +++ b/backend/open_webui/internal/config.py @@ -233,6 +233,11 @@ class AppConfig: except Exception as exc: log.error("Async persist failed for '%s': %s", name, exc) + async def commit(self, name: str) -> None: + """Await the durable DB write of one entry, raising on failure (__setattr__'s write is fire-and-forget).""" + if _persist_enabled: + await self._entries[name].commit_async() + def __getattr__(self, name: str) -> Any: entries = super().__getattribute__('_entries') if name not in entries: diff --git a/backend/open_webui/routers/configs.py b/backend/open_webui/routers/configs.py index c40131f41f..f2ef22bea3 100644 --- a/backend/open_webui/routers/configs.py +++ b/backend/open_webui/routers/configs.py @@ -197,6 +197,14 @@ async def set_tool_servers_config( connection.model_dump() for connection in form_data.TOOL_SERVER_CONNECTIONS ] + # The write above is fire-and-forget and swallows errors; confirm it reached disk so a + # persistence failure fails closed (HTTP 500) instead of returning a silent success. + try: + await request.app.state.config.commit('TOOL_SERVER_CONNECTIONS') + except Exception as e: + log.error(f'Failed to persist tool server connections: {e}') + raise HTTPException(status_code=500, detail='Failed to persist tool server configuration') + await set_tool_servers(request) for connection in request.app.state.config.TOOL_SERVER_CONNECTIONS: