Skip to content

feat(desktop): restart daemon from terminal-unreachable toast#4962

Open
Kitenite wants to merge 1 commit into
mainfrom
daemon-restart-toast-butt
Open

feat(desktop): restart daemon from terminal-unreachable toast#4962
Kitenite wants to merge 1 commit into
mainfrom
daemon-restart-toast-butt

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented May 28, 2026

Summary

  • Adds a persistent "Terminal daemon unreachable" toast that fires once after auto-reconnect exhausts its 5 retries.
  • Toast includes a Restart daemon action that calls the same electronTrpc.terminal.restartDaemon mutation used by Settings → Terminal → Manage sessions.
  • Toast state resets when the connection recovers, so it won't double-fire on subsequent failures.

Test plan

  • Kill host-service / pty-daemon while a terminal pane is open; confirm reconnect attempts fire, then the toast appears after retries exhaust.
  • Click Restart daemon in the toast; verify success toast appears and a fresh daemon starts on the next terminal action.
  • Toast does not re-fire on transient disconnects that recover within 5 retries.

Open in Stage

Summary by cubic

Adds a persistent "Terminal daemon unreachable" toast after 5 failed auto-reconnect attempts, with a Restart daemon action that calls electronTrpc.terminal.restartDaemon. The toast shows only once per failure, resets on recovery, and displays success/error feedback.

Written for commit 51fdacf. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Terminal now displays clearer notifications when the daemon becomes unreachable, with a quick restart action button available directly in the error message.
    • Improved terminal connection error state handling and feedback.

Review Change Stack

After auto-reconnect exhausts its 5 retries, surface a persistent toast
with a "Restart daemon" action so users can recover without digging into
Settings → Manage sessions.
@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 28, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@stage-review
Copy link
Copy Markdown

stage-review Bot commented May 28, 2026

Ready to review this PR? Stage has broken it down into 1 individual chapter for you:

Title
1 Implement daemon restart logic and toast notification
Open in Stage

Chapters generated by Stage for commit 51fdacf on May 28, 2026 12:08am UTC.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

Terminal.tsx now imports toast and adds a dedicated restart daemon mutation that shows success/error notifications. The connection retry effect resets the one-time daemon failure toast when there is no error and displays a single "unreachable" toast with an inline action button to restart the daemon when max retries are exceeded.

Changes

Terminal daemon restart UI and notifications

Layer / File(s) Summary
Daemon restart notification and mutation
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
Adds toast import and a restartDaemonMutation with success/error feedback. Connection auto-retry effect now clears the one-time toast flag when there is no error and displays a single "Terminal daemon unreachable" toast with an action button that triggers the restart mutation when max retries are exceeded.

Sequence Diagram

sequenceDiagram
  participant RetryEffect as Connection Retry Effect
  participant Toast as Toast Service
  participant User as User
  participant Mutation as Restart Daemon Mutation
  RetryEffect->>RetryEffect: Check max retries exceeded
  RetryEffect->>Toast: Show "unreachable" error toast
  Toast->>User: Display notification with action
  User->>Toast: Click restart action
  Toast->>Mutation: Trigger restart daemon
  Mutation->>Toast: Show success/error toast
  Toast->>User: Display result
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • superset-sh/superset#4793: Both PRs wire UI around daemon update failure by triggering the same "restart/restart daemon" mutation and showing toast-based success/error messaging when the daemon can't be reached after update attempts/polling.

Poem

A rabbit whispers through the terminal screen,
"When daemons fail, let users intervene!"
With toasts that glow and buttons to restart,
Connection blooms anew, a hopeful art. 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a restart daemon action to the terminal-unreachable toast.
Description check ✅ Passed The description covers the main functionality (toast with restart action), includes a test plan with specific steps, but omits the structured template sections (Related Issues, Type of Change, Testing, etc.).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch daemon-restart-toast-butt

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 28, 2026

Greptile Summary

This PR adds a persistent "Terminal daemon unreachable" toast that fires once after the 5-retry auto-reconnect cycle is exhausted, with a "Restart daemon" inline action that calls the same electronTrpc.terminal.restartDaemon mutation used in Settings. The toast is suppressed on subsequent failures until a successful reconnect resets the flag.

  • A daemonFailureToastShownRef guards against duplicate toasts per pane; the ref resets when connectionError clears so the toast can re-appear after a full recovery cycle.
  • The restartDaemonMutation is mirrored into a ref (restartDaemonMutationRef) so the toast's onClick closure always calls the latest mutation instance rather than a stale one captured at toast-creation time.
  • The toast is created with duration: Number.POSITIVE_INFINITY but there is no corresponding toast.dismiss() on component unmount, leaving a stale, actionable toast visible after the pane is closed.

Confidence Score: 3/5

The core reconnect logic and guard flag are sound, but a persistent infinite-duration toast is shown without any cleanup when the terminal pane is closed, leaving a stale "Restart daemon" button visible and actionable after the originating component is gone.

The duration: Number.POSITIVE_INFINITY toast is created without capturing the returned ID or providing any unmount cleanup. If the user closes the terminal pane while the toast is displayed, the toast remains visible indefinitely and still invokes the mutation on click. The existing useUpdateListener in this codebase solves the same problem with a stable toast ID and toast.dismiss(), so there is a clear, established pattern that was not applied here.

Terminal.tsx — specifically the toast creation block and the absence of a cleanup effect to dismiss the persistent toast on unmount.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx Adds a persistent Sonner toast for daemon unreachability with a "Restart daemon" action after 5 failed reconnect attempts; missing toast cleanup on unmount allows a stale toast to persist after the pane is closed

Sequence Diagram

sequenceDiagram
    participant T as Terminal Component
    participant E as useEffect (connectionError)
    participant S as Sonner Toast
    participant R as restartDaemon tRPC

    T->>E: connectionError set
    E->>E: "retryCountRef < MAX_RETRIES?"
    E->>T: setTimeout(handleRetryConnection, delay)
    T->>E: connectionError still set (retry fails)
    note over E: retryCountRef reaches MAX_RETRIES
    E->>S: "toast.error(duration=∞)"
    note over S: daemonFailureToastShownRef = true
    S-->>T: User clicks Restart daemon
    T->>R: restartDaemonMutationRef.current.mutate()
    R-->>S: onSuccess → toast.success
    T->>E: connectionError clears (data event)
    E->>E: "daemonFailureToastShownRef = false"
    note over S: Toast NOT dismissed on pane unmount
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx:299-315
**Persistent toast not dismissed on component unmount**

The toast is created with `duration: Number.POSITIVE_INFINITY` but no cleanup runs when this `Terminal` instance unmounts (e.g., user closes the pane). The toast stays visible and functional indefinitely. If the user then clicks "Restart daemon," the mutation fires from an unmounted component — the daemon restarts, but `onSuccess`/`onError` can run against a detached component lifecycle. The existing `useUpdateListener` in this codebase shows the correct pattern: assign a stable `id` to the toast and call `toast.dismiss(id)` in a `useEffect` cleanup. For example, store the returned toast ID in a ref (`const toastIdRef = useRef<string | number | null>(null)`) and add a `useEffect(() => () => { if (toastIdRef.current != null) toast.dismiss(toastIdRef.current); }, [])` to guarantee cleanup on unmount.

### Issue 2 of 2
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx:303-313
**Multiple failing panes accumulate persistent toasts**

Because each `Terminal` instance manages its own `daemonFailureToastShownRef` and shows the toast independently, opening 3 terminal panes when the daemon is unreachable results in 3 stacked "Terminal daemon unreachable" toasts, each with its own "Restart daemon" button. Since this is a daemon-level (not pane-level) failure, a shared toast ID (e.g., a module-level constant like `DAEMON_TOAST_ID`) would allow Sonner to deduplicate updates to the same toast rather than stacking them. This mirrors how `useUpdateListener` uses `UPDATE_TOAST_ID` to prevent duplicate update toasts.

Reviews (1): Last reviewed commit: "feat(desktop): offer daemon restart from..." | Re-trigger Greptile

Comment on lines 299 to +315
if (isExitedRef.current) return;
if (retryCountRef.current >= MAX_RETRIES) return;
if (retryCountRef.current >= MAX_RETRIES) {
if (!daemonFailureToastShownRef.current) {
daemonFailureToastShownRef.current = true;
toast.error("Terminal daemon unreachable", {
description:
"Couldn't reconnect after several attempts. Restart the daemon to recover.",
duration: Number.POSITIVE_INFINITY,
action: {
label: "Restart daemon",
onClick: () => {
restartDaemonMutationRef.current.mutate();
},
},
});
}
return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Persistent toast not dismissed on component unmount

The toast is created with duration: Number.POSITIVE_INFINITY but no cleanup runs when this Terminal instance unmounts (e.g., user closes the pane). The toast stays visible and functional indefinitely. If the user then clicks "Restart daemon," the mutation fires from an unmounted component — the daemon restarts, but onSuccess/onError can run against a detached component lifecycle. The existing useUpdateListener in this codebase shows the correct pattern: assign a stable id to the toast and call toast.dismiss(id) in a useEffect cleanup. For example, store the returned toast ID in a ref (const toastIdRef = useRef<string | number | null>(null)) and add a useEffect(() => () => { if (toastIdRef.current != null) toast.dismiss(toastIdRef.current); }, []) to guarantee cleanup on unmount.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
Line: 299-315

Comment:
**Persistent toast not dismissed on component unmount**

The toast is created with `duration: Number.POSITIVE_INFINITY` but no cleanup runs when this `Terminal` instance unmounts (e.g., user closes the pane). The toast stays visible and functional indefinitely. If the user then clicks "Restart daemon," the mutation fires from an unmounted component — the daemon restarts, but `onSuccess`/`onError` can run against a detached component lifecycle. The existing `useUpdateListener` in this codebase shows the correct pattern: assign a stable `id` to the toast and call `toast.dismiss(id)` in a `useEffect` cleanup. For example, store the returned toast ID in a ref (`const toastIdRef = useRef<string | number | null>(null)`) and add a `useEffect(() => () => { if (toastIdRef.current != null) toast.dismiss(toastIdRef.current); }, [])` to guarantee cleanup on unmount.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +303 to +313
toast.error("Terminal daemon unreachable", {
description:
"Couldn't reconnect after several attempts. Restart the daemon to recover.",
duration: Number.POSITIVE_INFINITY,
action: {
label: "Restart daemon",
onClick: () => {
restartDaemonMutationRef.current.mutate();
},
},
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Multiple failing panes accumulate persistent toasts

Because each Terminal instance manages its own daemonFailureToastShownRef and shows the toast independently, opening 3 terminal panes when the daemon is unreachable results in 3 stacked "Terminal daemon unreachable" toasts, each with its own "Restart daemon" button. Since this is a daemon-level (not pane-level) failure, a shared toast ID (e.g., a module-level constant like DAEMON_TOAST_ID) would allow Sonner to deduplicate updates to the same toast rather than stacking them. This mirrors how useUpdateListener uses UPDATE_TOAST_ID to prevent duplicate update toasts.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
Line: 303-313

Comment:
**Multiple failing panes accumulate persistent toasts**

Because each `Terminal` instance manages its own `daemonFailureToastShownRef` and shows the toast independently, opening 3 terminal panes when the daemon is unreachable results in 3 stacked "Terminal daemon unreachable" toasts, each with its own "Restart daemon" button. Since this is a daemon-level (not pane-level) failure, a shared toast ID (e.g., a module-level constant like `DAEMON_TOAST_ID`) would allow Sonner to deduplicate updates to the same toast rather than stacking them. This mirrors how `useUpdateListener` uses `UPDATE_TOAST_ID` to prevent duplicate update toasts.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx`:
- Around line 309-311: Guard the mutation call in the onClick handler by
checking the mutation's pending/loading state before invoking mutate: in
Terminal.tsx locate the onClick that calls
restartDaemonMutationRef.current.mutate() and change it to first read the ref
(e.g. const m = restartDaemonMutationRef.current) and return early if
m?.isPending || m?.isLoading (or the appropriate flag your mutation exposes);
only call m.mutate() (or m.mutateAsync()) when not pending. This prevents
concurrent restarts while keeping the existing restartDaemonMutationRef and
mutate call intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9249ff3f-11ea-4c8b-902a-ab0235bba249

📥 Commits

Reviewing files that changed from the base of the PR and between 4973c7a and 51fdacf.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx

Comment on lines +309 to +311
onClick: () => {
restartDaemonMutationRef.current.mutate();
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against concurrent mutation calls.

The action button can be clicked multiple times before the mutation completes, potentially triggering concurrent daemon restarts. Add an isPending check to prevent this.

🛡️ Proposed fix to add pending state guard
 					action: {
 						label: "Restart daemon",
 						onClick: () => {
+							if (!restartDaemonMutationRef.current.isPending) {
 								restartDaemonMutationRef.current.mutate();
+							}
 						},
 					},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onClick: () => {
restartDaemonMutationRef.current.mutate();
},
onClick: () => {
if (!restartDaemonMutationRef.current.isPending) {
restartDaemonMutationRef.current.mutate();
}
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx`
around lines 309 - 311, Guard the mutation call in the onClick handler by
checking the mutation's pending/loading state before invoking mutate: in
Terminal.tsx locate the onClick that calls
restartDaemonMutationRef.current.mutate() and change it to first read the ref
(e.g. const m = restartDaemonMutationRef.current) and return early if
m?.isPending || m?.isLoading (or the appropriate flag your mutation exposes);
only call m.mutate() (or m.mutateAsync()) when not pending. This prevents
concurrent restarts while keeping the existing restartDaemonMutationRef and
mutate call intact.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx">

<violation number="1" location="apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx:296">
P2: The persistent "Terminal daemon unreachable" toast is never dismissed on reconnect, so a stale error can remain visible after recovery.</violation>

<violation number="2" location="apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx:303">
P2: Multiple terminal panes will each show their own "Terminal daemon unreachable" toast since `daemonFailureToastShownRef` is per-instance and no shared `id` is passed to `toast.error()`. Use a module-level constant toast ID (e.g., `const DAEMON_UNREACHABLE_TOAST_ID = "daemon-unreachable"`) so Sonner deduplicates rather than stacking identical toasts.</violation>

<violation number="3" location="apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx:306">
P2: The infinite-duration toast is never dismissed when this component unmounts (e.g., user closes the terminal pane). The toast remains visible and functional with a stale ref. Assign a stable `id` to the toast and dismiss it in a `useEffect` cleanup to prevent interaction with an unmounted component's state.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Terminal as Terminal Component
    participant Retry as Auto-Reconnect Logic
    participant Toast as Toast System
    participant Mutation as restartDaemon Mutation
    participant Backend as Terminal Daemon (pty-host)

    Note over Terminal,Backend: Terminal Daemon Unreachable Flow

    Terminal->>Retry: Connection error detected
    Retry->>Retry: retryCountRef >= MAX_RETRIES (5)?
    alt Connection error cleared
        Retry->>Terminal: Reset daemonFailureToastShownRef
    else Retries exhausted
        Retry->>Retry: daemonFailureToastShownRef is false?
        alt Toast not yet shown
            Retry->>Toast: Show "Terminal daemon unreachable" toast (persistent)
            Toast->>Toast: Set daemonFailureToastShownRef = true
        end
        Note over Terminal,Toast: User clicks "Restart daemon"
        Toast->>Mutation: trigger restartDaemon mutation
        Mutation->>Backend: electronTrpc.terminal.restartDaemon
        Backend-->>Mutation: Restart result
        alt Success
            Mutation->>Toast: Show success toast "Daemon restarted"
        else Error
            Mutation->>Toast: Show error toast with error.message
        end
    end
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

toast.error("Terminal daemon unreachable", {
description:
"Couldn't reconnect after several attempts. Restart the daemon to recover.",
duration: Number.POSITIVE_INFINITY,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The infinite-duration toast is never dismissed when this component unmounts (e.g., user closes the terminal pane). The toast remains visible and functional with a stale ref. Assign a stable id to the toast and dismiss it in a useEffect cleanup to prevent interaction with an unmounted component's state.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx, line 306:

<comment>The infinite-duration toast is never dismissed when this component unmounts (e.g., user closes the terminal pane). The toast remains visible and functional with a stale ref. Assign a stable `id` to the toast and dismiss it in a `useEffect` cleanup to prevent interaction with an unmounted component's state.</comment>

<file context>
@@ -272,9 +292,28 @@ export const Terminal = memo(function Terminal({
+				toast.error("Terminal daemon unreachable", {
+					description:
+						"Couldn't reconnect after several attempts. Restart the daemon to recover.",
+					duration: Number.POSITIVE_INFINITY,
+					action: {
+						label: "Restart daemon",
</file context>

if (retryCountRef.current >= MAX_RETRIES) {
if (!daemonFailureToastShownRef.current) {
daemonFailureToastShownRef.current = true;
toast.error("Terminal daemon unreachable", {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Multiple terminal panes will each show their own "Terminal daemon unreachable" toast since daemonFailureToastShownRef is per-instance and no shared id is passed to toast.error(). Use a module-level constant toast ID (e.g., const DAEMON_UNREACHABLE_TOAST_ID = "daemon-unreachable") so Sonner deduplicates rather than stacking identical toasts.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx, line 303:

<comment>Multiple terminal panes will each show their own "Terminal daemon unreachable" toast since `daemonFailureToastShownRef` is per-instance and no shared `id` is passed to `toast.error()`. Use a module-level constant toast ID (e.g., `const DAEMON_UNREACHABLE_TOAST_ID = "daemon-unreachable"`) so Sonner deduplicates rather than stacking identical toasts.</comment>

<file context>
@@ -272,9 +292,28 @@ export const Terminal = memo(function Terminal({
+		if (retryCountRef.current >= MAX_RETRIES) {
+			if (!daemonFailureToastShownRef.current) {
+				daemonFailureToastShownRef.current = true;
+				toast.error("Terminal daemon unreachable", {
+					description:
+						"Couldn't reconnect after several attempts. Restart the daemon to recover.",
</file context>
Suggested change
toast.error("Terminal daemon unreachable", {
toast.error("Terminal daemon unreachable", {
id: "daemon-unreachable",

useEffect(() => {
if (!connectionError) return;
if (!connectionError) {
daemonFailureToastShownRef.current = false;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The persistent "Terminal daemon unreachable" toast is never dismissed on reconnect, so a stale error can remain visible after recovery.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx, line 296:

<comment>The persistent "Terminal daemon unreachable" toast is never dismissed on reconnect, so a stale error can remain visible after recovery.</comment>

<file context>
@@ -272,9 +292,28 @@ export const Terminal = memo(function Terminal({
 	useEffect(() => {
-		if (!connectionError) return;
+		if (!connectionError) {
+			daemonFailureToastShownRef.current = false;
+			return;
+		}
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant