Add restart state management and error handling to self-update overlay
Add resetRestartState helper to clear restart status/detail text. Set initial "Preparing update" state in scheduleUpdate before API call. Make restart warning toast non-blocking (catch errors without await). Distinguish restart request errors from connection errors in restartAndReload - only clear overlay for actual HTTP failures, not expected connection drops. Reset restart state and remove overlay on restart request failure or
frdel committed
Mar 26, 2026 at 13:10 UTC
e9d9c93d130604f0acba6d31fca1eba20c41b9c2
2 files changed
+35
-4
tests/test_self_update_tag_filter.py
+5
@@ -384,12 +384,17 @@ def test_self_update_frontend_uses_preloaded_select():
384
assert "response.pending || {" in content
385
assert "tag: \"\"," in content
386
assert "await this.fetchTags();" in content
387
+ assert '"Preparing update"' in content
388
+ assert '"Saving the request and asking Agent Zero to restart."' in content
389
assert "Release tag must use the format vX.Y." in content
390
assert "Release tag must be v1.0 or newer." in content
391
assert "isLatestSelectorTag(value)" in content
392
assert "this.isSelectableTag(this.form.tag)" in content
393
assert "getLastStatusBadgeClass(status)" in content
394
assert "this.info?.current?.display_version" in content
395
+ assert "resetRestartState()" in content
396
+ assert "restartRequestError" in content
397
+ assert "await notificationStore.frontendWarning(" not in content
398
assert "status-pill-error" in content
399
assert "status-pill-success" in content
400
assert "this.info?.defaults?.branch ||" in content
webui/components/settings/external/self-update-store.js
+30
-4
@@ -261,6 +261,11 @@ const model = {
261
document.getElementById(`${SELF_UPDATE_OVERLAY_ID}-styles`)?.remove();
262
},
263
264
+ resetRestartState() {
265
+ this.restartStatusText = "";
266
+ this.restartDetailText = "";
267
+ },
268
+
269
setRestartState(statusText, detailText = "") {
270
this.restartStatusText = statusText;
271
this.restartDetailText = detailText;
@@ -439,6 +444,11 @@ const model = {
444
445
this.saving = true;
446
this.error = "";
447
+ this.setRestartState(
448
+ "Preparing update",
449
+ "Saving the request and asking Agent Zero to restart."
450
+ );
451
+ this.ensureProgressOverlay();
452
try {
453
const response = await API.callJsonApi("self_update_schedule", {
454
branch: this.form.branch,
@@ -455,17 +465,22 @@ const model = {
465
if (this.info) {
466
this.info.pending = response.pending;
467
}
458
- await notificationStore.frontendWarning(
468
+ notificationStore.frontendWarning(
469
"Agent Zero is restarting to apply the requested branch and version target.",
470
"Self Update",
471
10,
472
"self-update-restart",
473
undefined,
474
true,
465
- );
475
+ ).catch((warningError) => {
476
+ console.error("Failed to show self-update warning toast:", warningError);
477
+ });
478
await this.restartAndReload();
479
} catch (error) {
480
console.error("Failed to schedule self-update:", error);
481
+ this.restarting = false;
482
+ this.resetRestartState();
483
+ this.removeProgressOverlay();
484
this.error = error.message || "Failed to schedule the self-update.";
485
} finally {
486
this.saving = false;
@@ -482,6 +497,7 @@ const model = {
497
);
498
this.ensureProgressOverlay();
499
500
+ let restartRequestError = null;
501
try {
502
const token = await API.getCsrfToken();
503
const restartResponse = await fetch("/api/restart", {
@@ -495,9 +511,18 @@ const model = {
511
body: JSON.stringify({}),
512
});
513
if (restartResponse && !restartResponse.ok) {
498
- throw new Error(`Restart request failed with HTTP ${restartResponse.status}.`);
514
+ restartRequestError = new Error(
515
+ `Restart request failed with HTTP ${restartResponse.status}.`
516
+ );
517
+ throw restartRequestError;
518
+ }
519
+ } catch (error) {
520
+ if (restartRequestError && error === restartRequestError) {
521
+ this.restarting = false;
522
+ this.resetRestartState();
523
+ this.removeProgressOverlay();
524
+ throw error;
525
}
500
- } catch (_error) {
526
// The restart request often terminates the backend mid-flight.
527
}
528
@@ -557,6 +582,7 @@ const model = {
582
}
583
584
this.restarting = false;
585
+ this.resetRestartState();
586
this.removeProgressOverlay();
587
this.error =
588
"Agent Zero did not come back within the expected window. It may still be rolling back. " +