Skip to content

Text Duplication in Chat Response UI #13658

Closed

Description

@albinartush

Description

Bug Report: Text Duplication in Chat Response UI

Summary

Sometime, AI-generated text responses were being duplicated in the opencode client UI, displaying the same content twice (e.g., "Direttive correttamente assimilate. Procedere con l'assimilazione dei tools disponibili?Direttive correttamente assimilate. Procedere con l'assimilazione dei tools disponibili?").

Root Cause Analysis

The duplication was caused by a dual rendering mechanism in the client-side UI component that was not properly synchronized:

1. Affected Component

File: packages/ui/src/components/session-turn.tsx

2. Rendering Architecture

The SessionTurn component renders assistant messages through two separate paths:

Path A: AssistantMessageItem (Line 729-747)

  • Renders all parts of assistant messages including text parts
  • Controlled by the hideResponsePart prop to filter out the last text part
  • Displays text during the streaming process

Path B: Summary Section (Line 756-793)

<Show when={!working() && response()}>
  <div data-slot="session-turn-summary-section">
    <Markdown text={response() ?? ""} cacheKey={responsePartId()} />
  </div>
</Show>
  • Renders the final response text at the bottom when working() is false
  • Intended to show a clean summary after streaming completes

3. The Bug

The hideResponsePart logic in the AssistantMessageItem component (Line 158-181) was not working correctly:

Original problematic code:

if (props.hideResponsePart) {
  const responsePartId = props.responsePartId
  if (responsePartId && responsePartId === lastTextPart()?.id) {
    parts = parts.filter((part) => part?.id !== responsePartId)
  }
}

Issues identified:

  1. The comparison responsePartId === lastTextPart()?.id was unreliable due to SolidJS reactivity timing
  2. lastTextPart() is a memo that might return a different reference or stale value
  3. The condition was too restrictive - it required both IDs to match exactly, which could fail during streaming state transitions

Impact

  • All streaming text responses were duplicated in the UI
  • User experience severely degraded with repeated text
  • Only affected the display layer, not the underlying data or API responses

Fix Applied

Modified the filtering logic in session-turn.tsx (Line 158-181):

Fixed code:

if (props.hideResponsePart) {
  const responsePartId = props.responsePartId
  const lastPart = lastTextPart()
  
  // DEBUG: Log for tracking duplication issues
  console.log("[SessionTurn] hideResponsePart:", props.hideResponsePart, 
              "responsePartId:", responsePartId, 
              "lastTextPart?.id:", lastPart?.id,
              "lastTextPart?.text:", lastPart?.text?.slice(0, 50))
  
  if (responsePartId) {
    // FIX: Always filter when hideResponsePart is true and we have a valid responsePartId
    // Don't compare with lastTextPart, filter directly by ID
    const filtered = parts.filter((part) => part?.id !== responsePartId)
    console.log("[SessionTurn] Parts filtered:", parts.length, "->", filtered.length)
    parts = filtered
  }
}

Key Changes:

  1. Removed unreliable comparison: No longer depends on responsePartId === lastTextPart()?.id
  2. Simplified logic: Filter by responsePartId directly when hideResponsePart is true
  3. Added debugging: Console logs to track filtering behavior during development

Verification Steps

  1. Build the opencode client: npm run build or pnpm build
  2. Start a new chat session
  3. Send any message to trigger AI response
  4. Observe that text appears only once, not duplicated
  5. Check browser console for [SessionTurn] debug logs

Additional Notes

  • The ChromeOpenAPIProxy server was working correctly and sending responses only once
  • This was purely a client-side rendering issue
  • The fix maintains backward compatibility with existing functionality
  • No changes required to the proxy or API layer

Related Files

  • Client UI: packages/ui/src/components/session-turn.tsx
  • Stream Processing: packages/opencode/src/session/processor.ts
  • Event Handling: packages/app/src/context/global-sync/event-reducer.ts

Plugins

No response

OpenCode version

No response

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. alexyaroshuk commented on Feb 14, 2026

    @alexyaroshuk
    Contributor

    ? "Fix applied"? What is this? AI output?

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions