siyuan-note/siyuan · warning
model stream idle timeout
Error message
model stream idle timeout
What it means
errModelStreamIdleTimeout is returned by recvStreamWithIdleTimeout when no chunk arrives from the model stream within streamIdleTimeout. A timer wraps stream.Recv(); if it fires before the next chunk (stopCancelTimer returns true), the read is aborted with this sentinel, which classifyRetry maps to the "timeout" retry class.
Solutions
- Increase the stream idle timeout in the AI provider settings to tolerate slow chunk gaps.
- Rely on the automatic retry (classifyRetry returns "timeout"); if retries exhaust, check provider status.
- Switch to a different provider/endpoint if streams repeatedly stall mid-generation.
- Check for proxies/load balancers that kill idle HTTP connections and bypass or reconfigure them.
Example fix
// before: aggressive idle timeout aborts slow streams resp, err := recvStreamWithIdleTimeout(stream, 5*time.Second, streamCancel) // after: tolerate slow chunk gaps resp, err := recvStreamWithIdleTimeout(stream, 60*time.Second, streamCancel)
Defensive patterns
Strategy: retry
Validate before calling
// ensure idle timeout exceeds worst observed inter-chunk gap
if streamIdleTimeout < 10*time.Second {
streamIdleTimeout = 60 * time.Second
} Try / catch
resp, err := recvStreamWithIdleTimeout(stream, streamIdleTimeout, streamCancel)
if err != nil {
if errors.Is(err, errModelStreamIdleTimeout) {
// recreate the stream via createStreamWithRetry; classifyRetry maps this to "timeout"
}
} Prevention
- Configure a streamIdleTimeout tolerant of slow chunk gaps (tens of seconds).
- Bypass or reconfigure proxies that kill idle connections.
- Prefer providers with reliable streaming; switch away if stalls are frequent.
- Keep automatic retry enabled so idle-timeout aborts are transparently restarted.
When it happens
Trigger: In agent.go (~line 2515/2598): stream.Recv() blocks longer than streamIdleTimeout after connection establishment — the provider opened the stream but stopped sending chunks (server hang, mid-generation stall) — so recvStreamWithIdleTimeout returns errModelStreamIdleTimeout; exercised by TestStreamIdleTimeoutAfterPartialResponse.
Common situations: Provider stalls mid-generation (long tool-output generation, overloaded backend); network middleboxes silently drop the connection without TCP FIN; extremely long time between the first token and later tokens; streamIdleTimeout configured too aggressively for a slow model.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- AI editor stream idle timeout
- compaction summary request failed
- compaction summary stream failed
- errModelRequestTimeout
- AI editor request timeout
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/dbbfb6083c5f64fe.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/agent/agent.go:2515
e := SessionEntry{
ID: id,
Type: "assistant",
Content: m.Content,
ReasoningCont: m.ReasoningContent,
ResponseOutput: util.CloneOpenAIResponseOutput(m.ResponseOutput),
ResponseOutputTokens: m.ResponseOutputTokens,
RoundID: m.RoundID,
ToolCalls: m.ToolCalls,
}
entries = append(entries, e)
}
}
return entries
}
var (
errModelRequestTimeout = errors.New("model request timeout")
errModelStreamIdleTimeout = errors.New("model stream idle timeout")
)
func createStreamWithRetry(ctx context.Context, client *openai.Client, req openai.ChatCompletionRequest, maxRetries int,
requestTimeout, streamIdleTimeout time.Duration, retryDelay func(string, int) time.Duration,
ch chan<- AgentEvent) (*util.OpenAICompletionStream, openai.ChatCompletionStreamResponse, context.CancelFunc, error) {
return createProtocolStreamWithRetry(ctx, client, util.OpenAIProtocolChatCompletions, req, nil, maxRetries,
requestTimeout, streamIdleTimeout, retryDelay, ch)
}
func createProtocolStreamWithRetry(ctx context.Context, client *openai.Client, protocol string,
req openai.ChatCompletionRequest, responseInput []any, maxRetries int, requestTimeout, streamIdleTimeout time.Duration,
retryDelay func(string, int) time.Duration,
ch chan<- AgentEvent) (*util.OpenAICompletionStream, openai.ChatCompletionStreamResponse, context.CancelFunc, error) {
if maxRetries < 0 {
maxRetries = 0
}
var lastErr errorView on GitHub (pinned to 9f775e8a12)