From 7b3e20d8a3aeddf08bb23b78de60297e66e213f0 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Sun, 22 Feb 2026 22:49:19 -0800 Subject: [PATCH] Fix retry race condition and error message formatting --- .../features/sessions/components/SessionView.tsx | 13 ++++++++++--- .../renderer/features/sessions/service/service.ts | 7 ++++--- .../features/sessions/stores/sessionStore.ts | 1 + .../task-detail/components/TaskLogsPanel.tsx | 5 +++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/twig/src/renderer/features/sessions/components/SessionView.tsx b/apps/twig/src/renderer/features/sessions/components/SessionView.tsx index 53b6869e6..3fcc1ddef 100644 --- a/apps/twig/src/renderer/features/sessions/components/SessionView.tsx +++ b/apps/twig/src/renderer/features/sessions/components/SessionView.tsx @@ -42,6 +42,7 @@ interface SessionViewProps { onCancelPrompt: () => void; repoPath?: string | null; hasError?: boolean; + errorTitle?: string; errorMessage?: string; onRetry?: () => void; onDelete?: () => void; @@ -62,6 +63,7 @@ export function SessionView({ onCancelPrompt, repoPath, hasError = false, + errorTitle, errorMessage = DEFAULT_ERROR_MESSAGE, onRetry, onDelete, @@ -380,11 +382,16 @@ export function SessionView({ className="absolute inset-0 bg-gray-1" > + {errorTitle && ( + + {errorTitle} + + )} {errorMessage} diff --git a/apps/twig/src/renderer/features/sessions/service/service.ts b/apps/twig/src/renderer/features/sessions/service/service.ts index cfef40d32..e0f46c596 100644 --- a/apps/twig/src/renderer/features/sessions/service/service.ts +++ b/apps/twig/src/renderer/features/sessions/service/service.ts @@ -268,7 +268,8 @@ export class SessionService { const taskRunId = latestRun?.id ?? `error-${taskId}`; const session = this.createBaseSession(taskRunId, taskId, taskTitle); session.status = "error"; - session.errorMessage = `Failed to connect to the agent: ${message}`; + session.errorTitle = "Failed to connect"; + session.errorMessage = message; if (latestRun?.log_url) { try { @@ -487,9 +488,11 @@ export class SessionService { taskRunId: string, taskTitle: string, errorMessage: string, + errorTitle?: string, ): void { const session = this.createBaseSession(taskRunId, taskId, taskTitle); session.status = "error"; + session.errorTitle = errorTitle; session.errorMessage = errorMessage; sessionStoreSetters.setSession(session); } @@ -1365,8 +1368,6 @@ export class SessionService { if (session) { await this.teardownSession(session.taskRunId); } - // Clear from connecting tasks as well - this.connectingTasks.delete(taskId); } /** diff --git a/apps/twig/src/renderer/features/sessions/stores/sessionStore.ts b/apps/twig/src/renderer/features/sessions/stores/sessionStore.ts index eca6501f9..5a30b0df5 100644 --- a/apps/twig/src/renderer/features/sessions/stores/sessionStore.ts +++ b/apps/twig/src/renderer/features/sessions/stores/sessionStore.ts @@ -36,6 +36,7 @@ export interface AgentSession { events: AcpMessage[]; startedAt: number; status: "connecting" | "connected" | "disconnected" | "error"; + errorTitle?: string; errorMessage?: string; isPromptPending: boolean; logUrl?: string; diff --git a/apps/twig/src/renderer/features/task-detail/components/TaskLogsPanel.tsx b/apps/twig/src/renderer/features/task-detail/components/TaskLogsPanel.tsx index cb94140b8..48c52146c 100644 --- a/apps/twig/src/renderer/features/task-detail/components/TaskLogsPanel.tsx +++ b/apps/twig/src/renderer/features/task-detail/components/TaskLogsPanel.tsx @@ -69,6 +69,7 @@ export function TaskLogsPanel({ taskId, task }: TaskLogsPanelProps) { const isRunning = session?.status === "connected" || session?.status === "connecting"; const hasError = session?.status === "error"; + const errorTitle = session?.errorTitle; const errorMessage = session?.errorMessage; const events = session?.events ?? []; @@ -236,8 +237,7 @@ export function TaskLogsPanel({ taskId, task }: TaskLogsPanelProps) { const handleRetry = useCallback(async () => { if (!repoPath) return; await getSessionService().clearSessionError(taskId); - getSessionService().connectToTask({ task, repoPath }); - }, [taskId, repoPath, task]); + }, [taskId, repoPath]); const handleDelete = useCallback(() => { const hasWorktree = workspace?.mode === "worktree"; @@ -322,6 +322,7 @@ export function TaskLogsPanel({ taskId, task }: TaskLogsPanelProps) { onCancelPrompt={handleCancelPrompt} repoPath={repoPath} hasError={isCloud ? false : hasError} + errorTitle={isCloud ? undefined : errorTitle} errorMessage={isCloud ? undefined : errorMessage} onRetry={handleRetry} onDelete={handleDelete}