parseStreamChatResponse ignores the [DONE] sentinel and hangs until read timeout on keep-alive servers
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 84/100
Research direction
Start with lib/Service/StreamingService.php, especially parseStreamChatResponse() around line 51 and parseSseEvent() around lines 114–116, where the issue says the [DONE] state is detected. Check for existing streaming service tests and run the relevant test; the issue does not name a test file. Done means the generator returns after [DONE] without waiting for the keep-alive connection to close, while preserving any final usage chunk.
Written by the indexing model from the issue text.
Description
Which version of integration_openai are you using?
5.0.0
Which version of Nextcloud are you using?
35.0.2
Which browser are you using? In case you are using the phone App, specify the Android or iOS version and device please.
Firefox 158.0
Describe the Bug
When streaming chat completions against a self-hosted OpenAI-compatible server that responds with:
Content-Type: text/event-stream,
Connection: keep-alive
and a correct data: [DONE] terminator, the stream is parsed fine (partial output appears in the UI) but never terminates.
After data: [DONE], the read loop in StreamingService::parseStreamChatResponse() (lib/Service/StreamingService.php:51) keeps calling fread() until the connection itself closes — which a keep-alive server does not do.
The generator stalls for the full service request timeout (Application::OPENAI_DEFAULT_REQUEST_TIMEOUT = 60 × 4 s), then fread() fails at StreamingService.php:54 and throws:
TaskProcessing core:text2text:multimodal-chatwithtools task failed: OpenAI/LocalAI request failed: Malformed API response
The loop already tracks end of stream correctly: parseSseEvent() sets $done = true when it parses data: [DONE] (line 114–116), but the while (!feof($body)) loop condition never consults $done. So stream completion is detected and then ignored, and "connection closed" is used as the stream-end signal. api.openai.com masks the bug because it closes the connection immediately after [DONE], so feof() arrives right behind the sentinel. Verified with curl that the server side is spec-clean: valid LF-framed SSE, valid JSON events, finish_reason + [DONE], proper chunked-transfer termination, Connection: keep-alive response header.
The systemd nextcloud-ai-worker is blocked for the 4 minute timeout period.
Proposed fix: change the loop condition to while (!$done && !feof($body)) (line 51) and fclose($body) once done, instead of waiting for a TCP close that compliant HTTP/1.1 servers have no obligation to send.
I added just the !$done and observed correct behavior in Assistant chats.
Expected Behavior
The generator should return immediately after parsing data: [DONE] (or a finish_reason event), regardless of whether the server keeps the TCP connection alive. The TaskProcessing task should complete with the full streamed result within the normal generation time, quota usage should be recorded from the final usage chunk if present, and no "Malformed API response" error should be raised. A keep-alive connection that stays open after a correctly terminated chunked body is compliant HTTP/1.1 and must not stall the client.
To Reproduce
Point a service at any OpenAI-compatible endpoint that streams SSE with keep-alive (this repro: self-hosted DGPP inference server, model nvidia/Qwen3.8-Flash-Next-NVFP4, Transfer-Encoding: chunked, Connection: keep-alive).
Confirm the stream itself is well-formed and finishes promptly with curl:
BASE_URL='http://<hostname>/v1'
MODEL='your/model'
KEY='APIKey'
curl -v -N "$BASE_URL/chat/completions" -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"model":"'"$MODEL"'","messages":[{"role":"user","content":"hi"}],"stream":true,"n":1}'
curl receives deltas, data: [DONE] and exits within seconds; response headers show Content-Type: text/event-stream and Connection: keep-alive, and curl reports the connection left intact.
In Nextcloud, trigger a streaming TaskProcessing task against the same service (e.g. core:text2text:multimodal-chatwithtools, preferStreaming: true). I typed "Hi" in Nextcloud assistant "Chat with AI".
Observe: output streams normally at first (might just be reasoning), then the task eventually fails with OpenAI/LocalAI request failed: Malformed API response — after ~4 min. For larger complex prompts the UI shows a complete-looking answer, then the same error. The delay equals the service request timeout (default 240 s = 60 × 4, AppInfo/Application.php:24).
- Dominant language
- PHP
- Stars
- 76
- Forks
- 34
- Avg merge
- 4d 54m
- Merged PRs (30d)
- 2
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
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 nextcloud/integration_openai
-
enhancement priority: normal
Difficulty 3/5 1-2 days Newbie friendliness 56/100
nextcloud/integration_openai#443 ·
-
Allow to send extra headers in the requests to the external servicePossibly taken @julien-nc claimed this 36 days ago. Openenhancement
nextcloud/integration_openai#436 · 1 assignee ·
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 68/100
nextcloud/integration_openai#426 · 6 comments ·
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 48/100
nextcloud/integration_openai#407 · 4 comments ·
-
enhancement
Difficulty 3/5 1-2 days Newbie friendliness 55/100
nextcloud/integration_openai#406 · 8 comments · 1 reaction ·
All issues in nextcloud/integration_openai
Similar issues
-
Successful AUTHORISATION for a transaction already set to "failed" is marked done without paying itOpen
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Adyen/adyen-shopware6#895 ·
Maintainers usually reply within 1 day
-
not confirmed
Difficulty 2/5 1-3 hours Newbie friendliness 63/100
Maintainers usually reply within 1 day
-
Debian 11: bullseye-security packages return 404 on security.debian.org; the apt template still points therePossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
MagnaCapax/PMSS#1119 · 1 comment ·
Maintainers usually reply within 3 days
-
Monit: poll time validation rejects "/", blocking the cron step syntax recommended by Monit 6Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 79/100
Maintainers usually reply within 1 day
-
[2.5] Admin GraphQL attributeValues.value reads price from integer_value instead of float_valueOpen
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
bagisto/bagisto-api#54 ·