From 94bc70495065abedf937fa98d2fab77021e5ac1f Mon Sep 17 00:00:00 2001 From: Developer Date: Fri, 10 Apr 2026 19:40:51 -0700 Subject: [PATCH] Fix settings save blocking event loop and overwriting config keys - Run apply_settings in thread pool executor to prevent engine reload from blocking the HTTP response (caused "TypeError: Failed to fetch") - Flatten nested config objects into dot-notation keys before saving so partial updates don't wipe out unincluded keys like auth_token Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/api_server.py | 6 +++++- backend/app_controller.py | 13 +++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/backend/api_server.py b/backend/api_server.py index fa5c583..2107bdd 100644 --- a/backend/api_server.py +++ b/backend/api_server.py @@ -212,7 +212,11 @@ class APIServer: @app.put("/api/config") async def update_config(update: ConfigUpdate): - engine_reloaded, message = ctrl.apply_settings(update.settings) + import asyncio + loop = asyncio.get_event_loop() + engine_reloaded, message = await loop.run_in_executor( + None, ctrl.apply_settings, update.settings + ) return { "status": "ok", "message": message, diff --git a/backend/app_controller.py b/backend/app_controller.py index 77b637d..d11670f 100644 --- a/backend/app_controller.py +++ b/backend/app_controller.py @@ -608,8 +608,17 @@ class AppController: Returns (engine_reload_needed, message). """ if new_config: - for key, value in new_config.items(): - self.config.set(key, value) + # Flatten nested dicts into dot-notation keys so we merge + # individual values instead of replacing entire sections + # (e.g. remote.mode instead of overwriting all of remote) + def _flatten(d, prefix=""): + for k, v in d.items(): + full_key = f"{prefix}{k}" if not prefix else f"{prefix}.{k}" + if isinstance(v, dict): + _flatten(v, full_key) + else: + self.config.set(full_key, v) + _flatten(new_config) # Update web server display settings if self.web_server: