Skip to content

fix: surface terminal-server connection errors in agent chat#7

Merged
DavidsonGomes merged 1 commit into
evolution-foundation:developfrom
gomessguii:fix/chat-connection-error-ui
Apr 13, 2026
Merged

fix: surface terminal-server connection errors in agent chat#7
DavidsonGomes merged 1 commit into
evolution-foundation:developfrom
gomessguii:fix/chat-connection-error-ui

Conversation

@gomessguii
Copy link
Copy Markdown
Member

@gomessguii gomessguii commented Apr 13, 2026

When the chat tab auto-creates a session via POST /api/sessions/for-agent, a silent .catch(() => {}) swallowed the fetch failure if terminal-server was unreachable. The parent left activeChatSessionId as null, AgentChat received sessionId=undefined, its connect effect early-returned, and the UI sat silently with a non-functional input — no feedback, no error.

Surface the failure through to the chat view:

  • AgentDetail tracks two new states, chatConnecting and chatConnectError, set around the find-or-create fetch. On failure the error message matches AgentTerminal's wording ("Could not reach terminal-server at {url}. Is it running?"). Both are passed down as externalLoading / externalError.

  • AgentChat accepts the two new optional props and folds them into a single effectiveError / inputDisabled. It renders a small amber "Connecting…" pill in the top-right corner while the session is being created, swaps it for a red error pill if the fetch fails, and disables the textarea (plus the send button via canSend) while either state is active. The rest of the chat layout stays mounted.

  • AgentChat also does its own HTTP preflight (GET /api/health) before opening the WebSocket, matching AgentTerminal's pattern, so a terminal-server that dies mid-session also flips to the same error pill instead of hanging forever on a WebSocket that never resolves.

  • Removed the center-bottom status text that the corner pill replaces.

Summary by Sourcery

Surface terminal-server connectivity failures in agent chat by propagating session creation errors to the chat UI and improving connection handling.

Bug Fixes:

  • Handle failures when auto-creating chat sessions so unreachable terminal-server errors are surfaced instead of leaving the chat input silently unusable.
  • Fail fast when terminal-server becomes unreachable mid-session by performing an HTTP health check before opening the WebSocket connection.

Enhancements:

  • Add explicit loading and error state wiring between AgentDetail and AgentChat, including a corner status pill and disabled input while connecting or on error.
  • Replace the bottom status text with a compact corner indicator that unifies connecting and error feedback for the chat view.

When the chat tab auto-creates a session via POST /api/sessions/for-agent,
a silent `.catch(() => {})` swallowed the fetch failure if terminal-server
was unreachable. The parent left `activeChatSessionId` as null, AgentChat
received `sessionId=undefined`, its connect effect early-returned, and the
UI sat silently with a non-functional input — no feedback, no error.

Surface the failure through to the chat view:

- AgentDetail tracks two new states, `chatConnecting` and `chatConnectError`,
  set around the find-or-create fetch. On failure the error message matches
  AgentTerminal's wording ("Could not reach terminal-server at {url}. Is it
  running?"). Both are passed down as `externalLoading` / `externalError`.

- AgentChat accepts the two new optional props and folds them into a single
  `effectiveError` / `inputDisabled`. It renders a small amber "Connecting…"
  pill in the top-right corner while the session is being created, swaps it
  for a red error pill if the fetch fails, and disables the textarea (plus
  the send button via `canSend`) while either state is active. The rest of
  the chat layout stays mounted.

- AgentChat also does its own HTTP preflight (`GET /api/health`) before
  opening the WebSocket, matching AgentTerminal's pattern, so a
  terminal-server that dies mid-session also flips to the same error pill
  instead of hanging forever on a WebSocket that never resolves.

- Removed the center-bottom status text that the corner pill replaces.
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 13, 2026

Reviewer's Guide

Surfaced terminal-server connectivity errors in the agent chat flow by propagating session creation failures from AgentDetail into AgentChat, adding HTTP health preflight before the WebSocket connection, unifying internal/external loading & error handling into a corner status pill, and disabling input while connection issues are present.

Sequence diagram for agent chat session creation and connectivity handling

sequenceDiagram
  actor User
  participant AgentDetail
  participant AgentChat
  participant TerminalServer_HTTP as TerminalServer_HTTP
  participant TerminalServer_WS as TerminalServer_WS

  User->>AgentDetail: Switch viewMode to chat
  AgentDetail->>AgentDetail: useEffect_createChatSession
  AgentDetail->>AgentDetail: set chatConnectError = null
  AgentDetail->>AgentDetail: set chatConnecting = true
  AgentDetail->>TerminalServer_HTTP: POST /api/sessions/for-agent
  alt Session creation succeeds
    TerminalServer_HTTP-->>AgentDetail: 200 OK + sessionId
    AgentDetail->>AgentDetail: add ChatSession to chatSessions
    AgentDetail->>AgentDetail: set activeChatSessionId
    AgentDetail->>AgentDetail: set chatConnecting = false
    AgentDetail->>AgentChat: Render with externalLoading=false externalError=null sessionId

    AgentChat->>AgentChat: useEffect_connectWebSocket
    AgentChat->>AgentChat: setStatus(connecting)
    AgentChat->>AgentChat: setErrorMsg(null)
    AgentChat->>TerminalServer_HTTP: GET /api/health
    alt Health check ok
      TerminalServer_HTTP-->>AgentChat: 200 OK
      AgentChat->>TerminalServer_WS: WebSocket connect /ws
      TerminalServer_WS-->>AgentChat: onopen
      AgentChat->>TerminalServer_WS: send join_session(sessionId)
      TerminalServer_WS-->>AgentChat: session_joined + optional chatHistory
      AgentChat->>AgentChat: setStatus(idle)
      AgentChat->>AgentChat: render status pill hidden
    else Health check fails
      TerminalServer_HTTP-->>AgentChat: Error or non 2xx
      AgentChat->>AgentChat: setStatus(error)
      AgentChat->>AgentChat: setErrorMsg("Could not reach terminal-server at TS_HTTP. Is it running?")
      AgentChat->>AgentChat: show error pill, disable input
    end
  else Session creation fails
    TerminalServer_HTTP-->>AgentDetail: Network error or non 2xx
    AgentDetail->>AgentDetail: set chatConnectError("Could not reach terminal-server at TS_HTTP. Is it running?")
    AgentDetail->>AgentDetail: set chatConnecting = false
    AgentDetail->>AgentChat: Render with externalLoading=false externalError=chatConnectError sessionId undefined
    AgentChat->>AgentChat: derive inputDisabled from externalError
    AgentChat->>AgentChat: show error pill, disable input
  end
Loading

Class diagram for AgentDetail and AgentChat connectivity state changes

classDiagram
  class AgentDetail {
    +ChatSession[] chatSessions
    +string activeChatSessionId
    +string chatConnectError
    +boolean chatConnecting
    +useEffect_createChatSession()
  }

  class ChatSession {
    +string sessionId
    +string title
    +number createdAt
  }

  class AgentChatProps {
    +string agent
    +string sessionId
    +string accentColor
    +boolean externalLoading
    +string externalError
  }

  class AgentChat {
    +ChatMessage[] messages
    +string input
    +Status status
    +boolean isThinking
    +string errorMsg
    +boolean isDragging
    +boolean isConnecting
    +string effectiveError
    +boolean inputDisabled
    +boolean canSend
    +useEffect_connectWebSocket()
    +handleChatEvent(event)
  }

  class ChatMessage {
    +string role
    +string text
    +number ts
    +boolean streaming
  }

  class Status {
    <<enumeration>>
    idle
    connecting
    running
    error
  }

  AgentDetail "1" o-- "*" ChatSession : manages
  AgentDetail "1" --> "1" AgentChat : renders_with_props
  AgentChat "1" -- "1" AgentChatProps : uses
  AgentChat "1" o-- "*" ChatMessage : displays
  AgentChat --> Status : has_status
Loading

File-Level Changes

Change Details Files
Propagate terminal-server session creation failures from AgentDetail into AgentChat and expose them as external loading/error state.
  • Added chatConnecting and chatConnectError state in AgentDetail to track auto-created chat session lifecycle.
  • Updated the auto-create chat session effect to treat non-OK responses as errors, set a human-readable error message on failure, and always clear the connecting flag in finally.
  • Passed the new chatConnecting and chatConnectError values into AgentChat via externalLoading and externalError props.
dashboard/frontend/src/pages/AgentDetail.tsx
Harden AgentChat connection handling with HTTP health preflight, unified status/error handling, and updated UI affordances.
  • Extended AgentChat props with optional externalLoading and externalError and derived isConnecting, effectiveError, and inputDisabled state from both internal and external sources.
  • Added a pre-WebSocket HTTP health check against TS_HTTP /api/health, with cancellation handling, and surfaced failures as a consistent terminal-server reachability error message.
  • Refactored WebSocket setup to respect cancellation, maintain existing chat event handling, and ensure ping timers and sockets are cleaned up safely.
  • Introduced a corner status pill that reflects connecting or error state, and removed the previous bottom status bar in favor of this indicator.
  • Changed textarea disabled logic and styles plus canSend computation to rely on inputDisabled so chat input and send are disabled while connecting or when an error is present.
  • Added TS_HTTP URL constant to parallel TS_WS for consistent terminal-server HTTP preflight and error messaging.
dashboard/frontend/src/components/AgentChat.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-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.

Hey - I've found 1 issue, and left some high level feedback:

  • The terminal-server HTTP base URL and health-preflight logic are now duplicated between AgentChat and AgentTerminal/AgentDetail; consider extracting a small shared helper/module so the URL construction and error messaging stay consistent in one place.
  • In the AgentChat WebSocket effect, you’re manually tracking cancelled; using an AbortController with fetch (and checking signal.aborted) would make cancellation more explicit and avoid potential future races if additional async steps are added.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The terminal-server HTTP base URL and health-preflight logic are now duplicated between AgentChat and AgentTerminal/AgentDetail; consider extracting a small shared helper/module so the URL construction and error messaging stay consistent in one place.
- In the AgentChat WebSocket effect, you’re manually tracking `cancelled`; using an `AbortController` with `fetch` (and checking `signal.aborted`) would make cancellation more explicit and avoid potential future races if additional async steps are added.

## Individual Comments

### Comment 1
<location path="dashboard/frontend/src/pages/AgentDetail.tsx" line_range="76" />
<code_context>
   useEffect(() => {
     if (!sessionId) return

</code_context>
<issue_to_address>
**issue (bug_risk):** Auto-create chat session effect can issue multiple POSTs if the user toggles view modes quickly

Because the effect depends on `viewMode`, `chatSessions.length`, and `name`, quickly toggling away from and back to `chat` while there is no session can fire multiple `POST /api/sessions/for-agent` requests in parallel, creating duplicate server sessions even though only the last response is used locally.

To prevent this, gate the call on a "request in flight" flag (e.g. `chatConnecting`) and add it to the dependency array:
```ts
if (viewMode === 'chat' && chatSessions.length === 0 && name && !chatConnecting) {
  ...
}
```
so a new request isn’t started while one is already pending.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

const [chatConnecting, setChatConnecting] = useState(false)

// Track agent visit for "Recent" section
useEffect(() => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Auto-create chat session effect can issue multiple POSTs if the user toggles view modes quickly

Because the effect depends on viewMode, chatSessions.length, and name, quickly toggling away from and back to chat while there is no session can fire multiple POST /api/sessions/for-agent requests in parallel, creating duplicate server sessions even though only the last response is used locally.

To prevent this, gate the call on a "request in flight" flag (e.g. chatConnecting) and add it to the dependency array:

if (viewMode === 'chat' && chatSessions.length === 0 && name && !chatConnecting) {
  ...
}

so a new request isn’t started while one is already pending.

@DavidsonGomes DavidsonGomes merged commit d74d194 into evolution-foundation:develop Apr 13, 2026
1 check passed
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.

2 participants