siyuan-note/siyuan · error
AI editor stream idle timeout
Error message
AI editor stream idle timeout
What it means
AIEditorChatStream.Recv (kernel/model/ai.go:144) arms an idle timer (fixed at 120 seconds in NewAIEditorChatStream) around every Recv on the underlying OpenAI streaming connection. If no SSE chunk arrives within that window, the timer cancels the stream context and Recv replaces the provider's error with this sentinel message. It guards the kernel against a provider that accepts the request but then stalls forever mid-generation.
Source
Thrown at kernel/model/ai.go:144
if "" == content || (openai.ChatMessageRoleUser != role && openai.ChatMessageRoleAssistant != role) {
continue
}
messages = append(messages, openai.ChatCompletionMessage{Role: role, Content: content})
}
return append(messages, openai.ChatCompletionMessage{Role: openai.ChatMessageRoleUser, Content: prompt})
}
type AIEditorChatStream struct {
stream *util.OpenAICompletionStream
cancel context.CancelFunc
idleTimeout time.Duration
}
func (stream *AIEditorChatStream) Recv() (response openai.ChatCompletionStreamResponse, err error) {
timer, timerDone := startAIEditorCancelTimer(stream.idleTimeout, stream.cancel)
response, err = stream.stream.Recv()
if stopAIEditorCancelTimer(timer, timerDone) {
err = errors.New("AI editor stream idle timeout")
}
return
}
func (stream *AIEditorChatStream) Close() {
stream.cancel()
stream.stream.Close()
}
// NewAIEditorChatStream 创建绑定到编辑器请求生命周期的模型流。
func NewAIEditorChatStream(ctx context.Context, ids []string, input, action string, history []AIEditorMessage) (*AIEditorChatStream, error) {
if !Conf.AI.HasAnyProvider() {
return nil, errors.New("no AI provider configured")
}
prov, m := Conf.AI.GetEditingModel()
if nil == prov || nil == m {
return nil, errors.New("no AI editing model configured")View on GitHub (pinned to afa823b6b4)
Solutions
- Check network stability to the provider endpoint, then simply retry the request — the stream is not resumable, it must be re-issued
- Shorten the work: reduce the selected content, prompt size, or the editing Max Completion Tokens so the model finishes faster
- Switch to a faster or closer provider/model that streams tokens steadily and sends SSE keep-alives
- If a proxy sits between kernel and provider, disable SSE buffering for text/event-stream
Example fix
// before
resp, err := stream.Recv() // one stall kills the whole interaction
// after
var resp openai.ChatCompletionStreamResponse
var err error
for attempt := 0; attempt < 2; attempt++ {
resp, err = stream.Recv()
if err == nil || err.Error() != "AI editor stream idle timeout" {
break
}
stream.Close()
stream, err = NewAIEditorChatStream(ctx, ids, input, action, history)
if err != nil {
break
}
} Defensive patterns
Strategy: retry
Try / catch
for {
resp, err := stream.Recv()
if err != nil {
if err.Error() == "AI editor stream idle timeout" {
stream.Close()
stream, err = NewAIEditorChatStream(ctx, ids, input, action, history)
if err != nil {
return err // config/network problem, stop retrying
}
continue // bounded by a max-attempt counter in production
}
return err
}
emit(resp)
} Prevention
- Bound retries (1-2 attempts) — an idle timeout mid-generation is usually a provider problem, not transient noise
- Keep selections and prompts compact so total generation stays well under provider stall tolerance
- Prefer providers/endpoints known to stream steadily and emit SSE keep-alives; avoid buffering proxies for text/event-stream
When it happens
Trigger: Streaming an AI editor chat completion where the model stops emitting tokens for more than 120 s: provider-side stall or queueing before the first/next token, dropped connection without RST, or an intermediary (proxy, gateway) that buffers SSE events.
Common situations: Self-hosted models under heavy load that pause between tokens; VPN/Wi-Fi drops mid-stream; corporate proxies buffering text/event-stream responses; very long reasoning phases on models that emit nothing while thinking.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- AI editor request timeout
- response stream ended before a terminal event
- response stream terminal event is missing response
- response failed
- response stream failed
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/14dfaa152772bc03.
Report an issue: GitHub.