Skip to content

Commit 1b05ca9

Browse files
kingpanther13claude
andcommitted
fix: treat dropped connection as success in restart handler
The Supervisor kills our process while the restart request is in flight, causing httpx to throw ReadError/RemoteProtocolError. That's actually the SUCCESS path — the restart is happening. Catch those specifically and return success. Also surface the real Supervisor error message in the browser when a real failure occurs (was showing generic "Restart failed" before). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 93a3e52 commit 1b05ca9

1 file changed

Lines changed: 30 additions & 9 deletions

File tree

src/ha_mcp/settings_ui.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -380,11 +380,18 @@ def apply_tool_visibility(
380380
if (resp.ok) {
381381
btn.textContent = 'Restart initiated — reload page in ~30s';
382382
} else {
383-
btn.textContent = 'Restart failed';
383+
let msg = 'Restart failed';
384+
try {
385+
const err = await resp.json();
386+
if (err.error && err.error.message) msg = 'Failed: ' + err.error.message;
387+
} catch (_e) {}
388+
btn.textContent = msg;
384389
btn.disabled = false;
390+
alert(msg);
385391
}
386392
} catch (_e) {
387-
btn.textContent = 'Connection lost (expected during restart)';
393+
// Connection lost mid-request is actually expected — the addon is restarting
394+
btn.textContent = 'Restart initiated (connection dropped)';
388395
}
389396
}
390397
@@ -672,24 +679,38 @@ async def _restart_addon(_: Request) -> JSONResponse:
672679
{"success": False, "error": {"code": "NOT_IN_ADDON", "message": "Restart only available in add-on mode"}},
673680
status_code=400,
674681
)
682+
# Short timeout — the supervisor kills our process during restart so
683+
# the connection will drop. A connection drop is actually success.
675684
try:
676-
async with httpx.AsyncClient(timeout=10.0) as client:
685+
async with httpx.AsyncClient(timeout=5.0) as client:
677686
resp = await client.post(
678687
"http://supervisor/addons/self/restart",
679688
headers={"Authorization": f"Bearer {token}"},
680689
)
681-
if resp.status_code >= 400:
682-
logger.error("Supervisor restart failed: %d %s", resp.status_code, resp.text)
683-
return JSONResponse(
684-
{"success": False, "error": {"code": "SUPERVISOR_ERROR", "message": f"Supervisor returned {resp.status_code}"}},
685-
status_code=502,
686-
)
690+
except (httpx.ReadError, httpx.RemoteProtocolError, httpx.ConnectError):
691+
# Connection dropped mid-request — restart is happening
692+
logger.info("Restart request connection dropped (expected during restart)")
693+
return JSONResponse({"success": True, "message": "Restart initiated"})
687694
except httpx.HTTPError as e:
688695
logger.exception("Failed to reach Supervisor for restart")
689696
return JSONResponse(
690697
{"success": False, "error": {"code": "SUPERVISOR_UNREACHABLE", "message": str(e)}},
691698
status_code=502,
692699
)
700+
701+
if resp.status_code >= 400:
702+
body = resp.text
703+
logger.error("Supervisor restart failed: %d %s", resp.status_code, body)
704+
return JSONResponse(
705+
{
706+
"success": False,
707+
"error": {
708+
"code": "SUPERVISOR_ERROR",
709+
"message": f"Supervisor returned {resp.status_code}: {body[:500]}",
710+
},
711+
},
712+
status_code=502,
713+
)
693714
return JSONResponse({"success": True, "message": "Restart initiated"})
694715

695716
@mcp.custom_route("/api/settings/info", methods=["GET"])

0 commit comments

Comments
 (0)