From 8cf6b03761e503cdc4a2da435b434afef138b136 Mon Sep 17 00:00:00 2001 From: aayurt Date: Sat, 13 Jun 2026 23:09:09 +0545 Subject: [PATCH 1/2] fix(instances): streamline instance stopping process and error handling --- packages/ui/src/stores/instances.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/ui/src/stores/instances.ts b/packages/ui/src/stores/instances.ts index e3bada698..0b1c5d2d0 100644 --- a/packages/ui/src/stores/instances.ts +++ b/packages/ui/src/stores/instances.ts @@ -720,14 +720,11 @@ async function stopInstance(id: string) { if (!instance) return releaseInstanceResources(id) + removeInstance(id) - try { - await serverApi.deleteWorkspace(id) - } catch (error) { + serverApi.deleteWorkspace(id).catch((error) => { log.error("Failed to stop workspace", error) - } - - removeInstance(id) + }) } async function fetchLspStatus(instanceId: string): Promise { From 86ac87a314a64918393123c82f9db0f26fc6a8ee Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Mon, 15 Jun 2026 09:36:42 +0100 Subject: [PATCH 2/2] fix(ui): clarify optimistic instance close Keep the tab-close fix optimistic by removing the instance from local state immediately, but make the backend workspace deletion explicitly fire-and-forget instead of exposing a misleading async stop contract. Remove stale awaits from call sites so callers no longer imply that stopInstance waits for backend teardown. If backend deletion fails after the tab is closed, surface the failure through the existing localized toast notification system while still logging the underlying error. Validation: npm run typecheck --workspace @codenomad/ui; npm run build --workspace @codenomad/ui; git diff --check. --- packages/ui/src/App.tsx | 2 +- packages/ui/src/components/session-picker.tsx | 2 +- packages/ui/src/lib/i18n/messages/de/app.ts | 1 + packages/ui/src/lib/i18n/messages/en/app.ts | 1 + packages/ui/src/lib/i18n/messages/es/app.ts | 1 + packages/ui/src/lib/i18n/messages/fr/app.ts | 1 + packages/ui/src/lib/i18n/messages/he/app.ts | 1 + packages/ui/src/lib/i18n/messages/ja/app.ts | 1 + packages/ui/src/lib/i18n/messages/ne/app.ts | 1 + packages/ui/src/lib/i18n/messages/ru/app.ts | 1 + packages/ui/src/lib/i18n/messages/zh-Hans/app.ts | 1 + packages/ui/src/stores/instances.ts | 12 +++++++++--- 12 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index 259eff7c3..bb2689cfc 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -389,7 +389,7 @@ const App: Component = () => { if (!confirmed) return - await stopInstance(instanceId) + stopInstance(instanceId) } async function handleNewSession(instanceId: string) { diff --git a/packages/ui/src/components/session-picker.tsx b/packages/ui/src/components/session-picker.tsx index 54de9c03f..e44d1024e 100644 --- a/packages/ui/src/components/session-picker.tsx +++ b/packages/ui/src/components/session-picker.tsx @@ -67,7 +67,7 @@ const SessionPicker: Component = (props) => { } async function handleCancel() { - await stopInstance(props.instanceId) + stopInstance(props.instanceId) props.onClose() } diff --git a/packages/ui/src/lib/i18n/messages/de/app.ts b/packages/ui/src/lib/i18n/messages/de/app.ts index f32815c82..d01d76899 100644 --- a/packages/ui/src/lib/i18n/messages/de/app.ts +++ b/packages/ui/src/lib/i18n/messages/de/app.ts @@ -12,6 +12,7 @@ export const appMessages = { "app.stopInstance.title": "Instanz stoppen", "app.stopInstance.confirmLabel": "Stoppen", "app.stopInstance.cancelLabel": "Weiterlaufen lassen", + "app.stopInstance.toast.error": "Arbeitsbereich konnte nicht gestoppt werden.", "emptyState.logoAlt": "CodeNomad Logo", "emptyState.brandTitle": "CodeNomad", diff --git a/packages/ui/src/lib/i18n/messages/en/app.ts b/packages/ui/src/lib/i18n/messages/en/app.ts index 510e5208a..1fd952478 100644 --- a/packages/ui/src/lib/i18n/messages/en/app.ts +++ b/packages/ui/src/lib/i18n/messages/en/app.ts @@ -12,6 +12,7 @@ export const appMessages = { "app.stopInstance.title": "Stop instance", "app.stopInstance.confirmLabel": "Stop", "app.stopInstance.cancelLabel": "Keep running", + "app.stopInstance.toast.error": "Failed to stop workspace.", "emptyState.logoAlt": "CodeNomad logo", "emptyState.brandTitle": "CodeNomad", diff --git a/packages/ui/src/lib/i18n/messages/es/app.ts b/packages/ui/src/lib/i18n/messages/es/app.ts index 554a59100..70303adee 100644 --- a/packages/ui/src/lib/i18n/messages/es/app.ts +++ b/packages/ui/src/lib/i18n/messages/es/app.ts @@ -12,6 +12,7 @@ export const appMessages = { "app.stopInstance.title": "Detener instancia", "app.stopInstance.confirmLabel": "Detener", "app.stopInstance.cancelLabel": "Seguir ejecutándose", + "app.stopInstance.toast.error": "No se pudo detener el workspace.", "emptyState.logoAlt": "Logo de CodeNomad", "emptyState.brandTitle": "CodeNomad", diff --git a/packages/ui/src/lib/i18n/messages/fr/app.ts b/packages/ui/src/lib/i18n/messages/fr/app.ts index 46349919a..013357180 100644 --- a/packages/ui/src/lib/i18n/messages/fr/app.ts +++ b/packages/ui/src/lib/i18n/messages/fr/app.ts @@ -12,6 +12,7 @@ export const appMessages = { "app.stopInstance.title": "Arrêter l'instance", "app.stopInstance.confirmLabel": "Arrêter", "app.stopInstance.cancelLabel": "Laisser tourner", + "app.stopInstance.toast.error": "Impossible d'arrêter l'espace de travail.", "emptyState.logoAlt": "Logo CodeNomad", "emptyState.brandTitle": "CodeNomad", diff --git a/packages/ui/src/lib/i18n/messages/he/app.ts b/packages/ui/src/lib/i18n/messages/he/app.ts index 487f185db..e00b6963d 100644 --- a/packages/ui/src/lib/i18n/messages/he/app.ts +++ b/packages/ui/src/lib/i18n/messages/he/app.ts @@ -12,6 +12,7 @@ export const appMessages = { "app.stopInstance.title": "עצור מופע", "app.stopInstance.confirmLabel": "עצור", "app.stopInstance.cancelLabel": "המשך להריץ", + "app.stopInstance.toast.error": "עצירת סביבת העבודה נכשלה.", "emptyState.logoAlt": "לוגו CodeNomad", "emptyState.brandTitle": "CodeNomad", diff --git a/packages/ui/src/lib/i18n/messages/ja/app.ts b/packages/ui/src/lib/i18n/messages/ja/app.ts index e96bf3cac..a16d89985 100644 --- a/packages/ui/src/lib/i18n/messages/ja/app.ts +++ b/packages/ui/src/lib/i18n/messages/ja/app.ts @@ -12,6 +12,7 @@ export const appMessages = { "app.stopInstance.title": "インスタンスを停止", "app.stopInstance.confirmLabel": "停止", "app.stopInstance.cancelLabel": "実行を続ける", + "app.stopInstance.toast.error": "ワークスペースの停止に失敗しました。", "emptyState.logoAlt": "CodeNomad ロゴ", "emptyState.brandTitle": "CodeNomad", diff --git a/packages/ui/src/lib/i18n/messages/ne/app.ts b/packages/ui/src/lib/i18n/messages/ne/app.ts index 01c614ff7..9bcf7ace1 100644 --- a/packages/ui/src/lib/i18n/messages/ne/app.ts +++ b/packages/ui/src/lib/i18n/messages/ne/app.ts @@ -12,6 +12,7 @@ export const appMessages = { "app.stopInstance.title": "उदाहरण रोक्नुहोस्", "app.stopInstance.confirmLabel": "रोक्नुहोस्", "app.stopInstance.cancelLabel": "चालु राख्नुहोस्", + "app.stopInstance.toast.error": "कार्यस्थान रोक्न असफल भयो।", "emptyState.logoAlt": "CodeNomad लोगो", "emptyState.brandTitle": "CodeNomad", diff --git a/packages/ui/src/lib/i18n/messages/ru/app.ts b/packages/ui/src/lib/i18n/messages/ru/app.ts index 1f8c41cd5..334b74c1b 100644 --- a/packages/ui/src/lib/i18n/messages/ru/app.ts +++ b/packages/ui/src/lib/i18n/messages/ru/app.ts @@ -12,6 +12,7 @@ export const appMessages = { "app.stopInstance.title": "Остановить экземпляр", "app.stopInstance.confirmLabel": "Остановить", "app.stopInstance.cancelLabel": "Оставить запущенным", + "app.stopInstance.toast.error": "Не удалось остановить рабочее пространство.", "emptyState.logoAlt": "Логотип CodeNomad", "emptyState.brandTitle": "CodeNomad", diff --git a/packages/ui/src/lib/i18n/messages/zh-Hans/app.ts b/packages/ui/src/lib/i18n/messages/zh-Hans/app.ts index cd2a82f86..dfff95213 100644 --- a/packages/ui/src/lib/i18n/messages/zh-Hans/app.ts +++ b/packages/ui/src/lib/i18n/messages/zh-Hans/app.ts @@ -12,6 +12,7 @@ export const appMessages = { "app.stopInstance.title": "停止实例", "app.stopInstance.confirmLabel": "停止", "app.stopInstance.cancelLabel": "继续运行", + "app.stopInstance.toast.error": "无法停止工作区。", "emptyState.logoAlt": "CodeNomad 徽标", "emptyState.brandTitle": "CodeNomad", diff --git a/packages/ui/src/stores/instances.ts b/packages/ui/src/stores/instances.ts index 0b1c5d2d0..96fddf6dd 100644 --- a/packages/ui/src/stores/instances.ts +++ b/packages/ui/src/stores/instances.ts @@ -54,6 +54,8 @@ import { mergeInstanceMetadata, clearInstanceMetadata } from "./instance-metadat import { showWorkspaceLaunchError } from "./launch-errors" import { activeSidecarToken } from "./sidecars" import { buildV2RequestLocations, type V2Location } from "./request-locations" +import { showToastNotification } from "../lib/notifications" +import { tGlobal } from "../lib/i18n" const log = getLogger("api") @@ -715,15 +717,19 @@ function updateProjectNameForFolder(folder: string, projectName: string): void { } } -async function stopInstance(id: string) { +function stopInstance(id: string) { const instance = instances().get(id) if (!instance) return releaseInstanceResources(id) removeInstance(id) - serverApi.deleteWorkspace(id).catch((error) => { + void serverApi.deleteWorkspace(id).catch((error) => { log.error("Failed to stop workspace", error) + showToastNotification({ + message: tGlobal("app.stopInstance.toast.error"), + variant: "error", + }) }) } @@ -1348,7 +1354,7 @@ async function acknowledgeDisconnectedInstance(): Promise { } try { - await stopInstance(pending.id) + stopInstance(pending.id) } catch (error) { log.error("Failed to stop disconnected instance", error) } finally {