CopilotKit / CopilotKit/CopilotKit

[v2] Message view freezes mid-run in long multi-tool runs (LangGraphAgent); state updates flow but UI stops re-rendering

Open
#6,301 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
37.4k
Forks
4.6k
Avg merge
1d 6h
Merged PRs (30d)
444

Description

Environment

  • @copilotkit/react-core / react-ui / runtime: 1.64.2 (all v2 imports)
  • Agent: LangGraphAgent (LangGraph dev server, stream_mode=['events','values','updates','messages-tuple'])
  • LLM: DeepSeek (reasoning model) via OpenAI-compatible API
  • Next.js 16 + React 19, production build

Symptom

In long multi-tool runs (e.g. query tool → long reasoning → file-generation tool → final answer), the chat view stops re-rendering mid-run while the run is still executing:

  • The last visible content is typically the assistant text right before a tool call.
  • The run completes successfully server-side (verified in traces and server logs).
  • The client keeps receiving and processing events — we instrumented agent.subscribe({ onEvent }) and events (STATE_SNAPSHOT, RAW, …) keep flowing after the UI freezes.
  • No console errors.
  • After the freeze, nothing brings the content back — waiting past run completion does not help. Only starting a new run flushes the accumulated content into view.

What we verified (elimination)

  • Server SSE stream is complete and well-formed (replayed the run: all ~2000 events valid JSON, RUN_FINISHED present).
  • Component renderers are fine in isolation (SSR smoke tests with the exact tool payloads).
  • Not throttling-related: we don't set throttleMs/defaultThrottleMs, so the unthrottled path is used.
  • A "reconcile watchdog" subscriber that returns { messages: [...messages] } (fresh array reference) on onRunFinishedEvent (outcome success) / onRunFailed / onRunErrorEvent does not unfreeze the view — suggesting the blockage is deeper than the MemoizedAssistantMessage reference comparison.
  • Two empirical red lines we hit while experimenting (both reproduce reliably):
    1. Returning a messages mutation from a subscriber while the run is paused on an interrupt breaks interrupt UI (the choice card from useInterrupt never renders).
    2. Returning periodic messages mutations during an active run corrupts in-flight streaming (the view freezes almost immediately).

Repro characteristics

  • Intermittent for short runs; near-deterministic for long runs (multiple tool calls + 20–60s reasoning + a tool whose arguments are a large JSON payload, ~10–20 KB streamed as TOOL_CALL_ARGS deltas).
  • Freeze point in our logs consistently lands right after a generate_file-like tool call's result, or right after an assistant message that carries both visible text and tool calls.
  • Our integration details that may matter: assistant messages from DeepSeek carry content as an array of parts (reasoning_content + text); heavy STATE_SNAPSHOT traffic (500+ per run) from LangGraph values stream mode.

Happy to provide

  • Full client-side event log (window.__evts probe) vs. server SSE comparison for a frozen run
  • Langfuse trace showing the run completing successfully
  • A minimal repro if you can point us at a reference LangGraph + v2 setup

Ask

  1. Is this a known class of issue in v2's message reconciliation (memoized message view vs. in-place state mutation)?
  2. Is there a supported way to force the message view to re-sync from agent.messages after a run? (Our subscriber mutation does not achieve this.)
  3. Guidance on the two red lines above — are subscriber messages mutations during interrupts / mid-run supposed to be safe?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the long multi-tool run and trace agent.subscribe({ onEvent }), agent.messages, and the MemoizedAssistantMessage and useInterrupt paths. Compare the event flow after TOOL_CALL_ARGS, STATE_SNAPSHOT, and RUN_FINISHED with the rendered message state. Done means the message view continues updating through long runs without breaking interrupt UI or streaming reconciliation.

Written by the indexing model from the issue text.

Assessment

Tech stack
next.js, react, typescript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.