react-headless: stream adapters end turns silently on in-stream errors, truncation, refusals and some wire variants

Open
#1,312 0 comments 0 reactions 1 assignee View on GitHub

Maintainers usually reply within 1 day

@ankit-thesys is already working on this.

Since Oct 7, 2026.

  • #1286 by @ankit-thesys — open

Assessment

This issue has not been assessed yet.

Description

Describe the bug

Several wire shapes make a @openuidev/react-headless stream adapter end a turn silently: no message and no error. In other cases they render a failed or cut-off answer as if it succeeded, or drop a tool call. With AgentInterface, the user sees the loader stop and nothing happen, or sees an answer that looks complete when it isn't.

Reproduced on the published @openuidev/react-headless 0.16.3 and 0.17.0, which have identical adapters, using the real AgentInterface from @openuidev/react-ui:

adapter wire shape what the chat shows
openAIAdapter, openAIReadableStreamAdapter, agUIAdapter, openAIResponsesAdapter an OpenAI-style {"error":{"message":…,"code":…}} record inside an HTTP 200 stream, as the OpenAI SDK, OpenRouter and the OpenUI Gateway send for a rejected request, an upstream provider failure or a post-stream failure blank turn, or the partial answer with no error
agUIAdapter, openAIAdapter, openAIResponsesAdapter, vercelAIAdapter a route that answers a failure with a plain JSON body (application/json, HTTP 200) blank turn
agUIAdapter, openAIAdapter, openAIResponsesAdapter data:{…} without the space after the colon (optional per the SSE spec; Go fmt.Fprintf, several Python frameworks) blank turn
openAIAdapter, openAIReadableStreamAdapter finish_reason: "length" / "content_filter" a truncated or filtered answer that looks complete
openAIAdapter, openAIReadableStreamAdapter delta.refusal empty turn
openAIAdapter, openAIReadableStreamAdapter a tool-call turn that finishes with finish_reason: "stop" (Gemini, some OpenAI-compatible proxies) the tool call never leaves "streaming"
openAIResponsesAdapter response.refusal.delta empty turn
openAIResponsesAdapter response.incomplete (max_output_tokens, content_filter) a truncated answer that looks complete
openAIResponsesAdapter file_search_call, code_interpreter_call and image_generation_call items no tool activity at all
vercelAIAdapter an abort chunk; finish with finishReason: "length" / "content-filter" a partial answer that looks complete
langGraphAdapter CRLF line endings (valid SSE; some reverse proxies and Windows hosts) zero events, blank turn
langGraphAdapter complete tool_calls with tool_call_chunks: [] (LangChain normalises a missing field to []) no tool call; the tool result is orphaned
eveAdapter action.result with status: "failed" the tool shows as successful, with an error object as its result

To Reproduce

Return any of the shapes above from a route and render it with AgentInterface and the matching adapter. Example for the first row with fetchLLM + openAIAdapter():

data: {"error":{"message":"The model provider failed to serve this request.","type":"invalid_request_error","code":"500"}}

Expected behavior

  • A failure, a truncation or an abort surfaces as an AG-UI RUN_ERROR, so AgentInterface shows its error callout. Text that already streamed stays.
  • A refusal renders as the answer text.
  • Tool calls always reach a terminal state, including hosted Responses tools.
  • A failed tool shows as failed.

Additional context

  • In the OpenAI Completions adapters, the error record has no choices, so if (!delta) continue skips it.
  • agUIAdapter yields it with no type, which processStreamedMessage ignores.
  • openAIResponsesAdapter handles the typed {"type":"error"} event, but drops an untyped record in default.

I have a fix ready to link here.

Dominant language
TypeScript
Stars
10k
Forks
698
Avg merge
1d 2h
Merged PRs (30d)
138

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

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.

More from thesysdev/openui

All issues in thesysdev/openui

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.