Hmbown/CodeWhale · error · anyhow::Error

Chat Completions stream closed before [DONE] or…

Error message

Chat Completions stream closed before [DONE] or finish_reason

What it means

After the SSE stream ends normally (no transport failure), the code checks whether a `[DONE]` sentinel or a `finish_reason` was observed. If neither appeared, the stream closed prematurely — the generation never formally terminated — so this error is yielded instead of `MessageStop`.

Solutions

  1. Treat as an incomplete generation: check whether the assistant message content is usable or should be retried
  2. Check server/proxy (nginx `proxy_read_timeout`, LB idle) timeouts and raise them
  3. Verify the OpenAI-compatible server sends a terminating `finish_reason` chunk and `[DONE]`
  4. Retry the request; enable streaming retry logic

Example fix

// before: nginx closing long idle streams
proxy_read_timeout 60s;
// after
proxy_read_timeout 300s;
proxy_buffering off;
Defensive patterns

Strategy: retry

Try / catch

match result {
    Err(e) if e.to_string().contains("closed before [DONE]") => {
        // incomplete generation: retry, or surface partial output with a warning
        retry_with_backoff(e);
    }
    other => propagate(other),
}

Prevention

When it happens

Trigger: The server closes the connection after partial output without sending `data: [DONE]` and without any choice carrying a `finish_reason` — e.g. server-side abort, max-tokens kill without a final chunk, proxy closing idle keep-alive early, or an OpenAI-compatible server that ends streams uncleanly.

Common situations: vLLM/Ollama or other self-hosted servers dropping the final chunk under load; reverse proxies (nginx) closing streams on idle timeouts; provider outages truncating generations.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/80a18e81c3594115. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/client/chat.rs:1688

                                yield Ok(event);
                            }
                        }
                    }
                }
            }

            // Close any open blocks — content_index points to the
            // currently active open block (it is only incremented
            // *after* a block is closed, not when opened).
            if thinking_started || text_started {
                yield Ok(StreamEvent::ContentBlockStop { index: content_index });
            }

            release_stream_buffer(byte_buf);
            if !stream_failed && (saw_done || saw_finish_reason) {
                yield Ok(StreamEvent::MessageStop);
            } else if !stream_failed {
                yield Err(anyhow::anyhow!(
                    "Chat Completions stream closed before [DONE] or finish_reason"
                ));
            }
        };

        Ok(Pin::from(Box::new(stream)
            as Box<
                dyn futures_util::Stream<Item = Result<StreamEvent>> + Send,
            >))
    }
}

// === Chat Completions Helpers ===

#[cfg(test)]
pub(super) fn build_chat_messages(
    system: Option<&SystemPrompt>,
    messages: &[Message],

View on GitHub (pinned to 73e0f67d83)