react-headless: stream adapters end turns silently on in-stream errors, truncation, refusals and some wire variants
Maintainers usually reply within 1 day
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, soAgentInterfaceshows 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, soif (!delta) continueskips it. agUIAdapteryields it with notype, whichprocessStreamedMessageignores.openAIResponsesAdapterhandles the typed{"type":"error"}event, but drops an untyped record indefault.
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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from thesysdev/openui
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
thesysdev/openui#1445 · 2 comments ·
Maintainers usually reply within 1 day
-
Add dataInvention flag to PromptOptions to disable the 'generate realistic/plausible data' ruleOpen
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
thesysdev/openui#959 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 18/100
Maintainers usually reply within 1 day
All issues in thesysdev/openui
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
caprover/caprover-frontend#273 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
ai-driven-dev/framework#1147 ·
Maintainers usually reply within 1 day
-
agent-ui bug p2
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
cybersemics/em#6232 ·
Maintainers usually reply within 1 day