From deb22bec0faca03131135e76fb2919766021c465 Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Sun, 24 May 2026 19:40:42 +0000 Subject: [PATCH] fix: prevent duplicate session action requests causing 404s - Add early return guards in all session action handlers (start, stop, delete, recreate tunnel) - Prevents race conditions where double-clicks or rapid clicks fire duplicate API calls - First delete succeeds, second would 404 because instance is already deleted - Applied to both sessions page and dashboard/home page --- apps/web/src/pages/dashboard.tsx | 4 ++++ apps/web/src/pages/sessions.tsx | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/apps/web/src/pages/dashboard.tsx b/apps/web/src/pages/dashboard.tsx index 3071d1f..1b6d99e 100644 --- a/apps/web/src/pages/dashboard.tsx +++ b/apps/web/src/pages/dashboard.tsx @@ -143,6 +143,7 @@ export const HomePage = () => { }; const handleStop = async (session: SessionView) => { + if (actionBusy === session.id) return; setActionBusy(session.id); try { await stopInstance(session.project_id, session.repository_id, session.id); @@ -153,6 +154,7 @@ export const HomePage = () => { }; const handleDelete = async (session: SessionView) => { + if (actionBusy === session.id) return; setActionBusy(session.id); try { await deleteInstance(session.project_id, session.repository_id, session.id); @@ -165,6 +167,7 @@ export const HomePage = () => { }; const handleRecreateTunnel = async (session: SessionView) => { + if (actionBusy === session.id) return; setActionBusy(session.id); try { await recreateInstanceTunnel(session.project_id, session.repository_id, session.id); @@ -175,6 +178,7 @@ export const HomePage = () => { }; const handleStart = async (session: SessionView) => { + if (actionBusy === session.id) return; setActionBusy(session.id); try { await startInstance(session.project_id, session.repository_id, session.id); diff --git a/apps/web/src/pages/sessions.tsx b/apps/web/src/pages/sessions.tsx index 3933fae..22ac603 100644 --- a/apps/web/src/pages/sessions.tsx +++ b/apps/web/src/pages/sessions.tsx @@ -155,6 +155,7 @@ export const SessionsPage = () => { }; const handleStop = async (session: Session) => { + if (loadingSessionId === session.id) return; setLoadingSessionId(session.id); try { await stopInstance(session.project_id, session.repository_id, session.id); @@ -167,6 +168,7 @@ export const SessionsPage = () => { }; const handleDelete = async (session: Session) => { + if (loadingSessionId === session.id) return; setLoadingSessionId(session.id); try { await deleteInstance(session.project_id, session.repository_id, session.id); @@ -190,6 +192,7 @@ export const SessionsPage = () => { }; const handleForceDelete = async (session: Session) => { + if (loadingSessionId === session.id) return; setLoadingSessionId(session.id); try { await deleteInstance(session.project_id, session.repository_id, session.id, true); @@ -204,6 +207,7 @@ export const SessionsPage = () => { }; const handleRecreateTunnel = async (session: Session) => { + if (loadingSessionId === session.id) return; setLoadingSessionId(session.id); try { await recreateInstanceTunnel( @@ -221,6 +225,7 @@ export const SessionsPage = () => { }; const handleStart = async (session: Session) => { + if (loadingSessionId === session.id) return; setLoadingSessionId(session.id); try { await startInstance(session.project_id, session.repository_id, session.id);