{"record":{"id":"dbbfb6083c5f64fe","repo":"siyuan-note/siyuan","slug":"model-stream-idle-timeout","errorCode":null,"errorMessage":"model stream idle timeout","messagePattern":"model stream idle timeout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/agent/agent.go","lineNumber":2445,"sourceCode":"\t\t\te := SessionEntry{\n\t\t\t\tID:                   id,\n\t\t\t\tType:                 \"assistant\",\n\t\t\t\tContent:              m.Content,\n\t\t\t\tReasoningCont:        m.ReasoningContent,\n\t\t\t\tResponseOutput:       util.CloneOpenAIResponseOutput(m.ResponseOutput),\n\t\t\t\tResponseOutputTokens: m.ResponseOutputTokens,\n\t\t\t\tRoundID:              m.RoundID,\n\t\t\t\tToolCalls:            m.ToolCalls,\n\t\t\t}\n\t\t\tentries = append(entries, e)\n\t\t}\n\t}\n\treturn entries\n}\n\nvar (\n\terrModelRequestTimeout    = errors.New(\"model request timeout\")\n\terrModelStreamIdleTimeout = errors.New(\"model stream idle timeout\")\n)\n\nfunc createStreamWithRetry(ctx context.Context, client *openai.Client, req openai.ChatCompletionRequest, maxRetries int,\n\trequestTimeout, streamIdleTimeout time.Duration, retryDelay func(string, int) time.Duration,\n\tch chan<- AgentEvent) (*util.OpenAICompletionStream, openai.ChatCompletionStreamResponse, context.CancelFunc, error) {\n\treturn createProtocolStreamWithRetry(ctx, client, util.OpenAIProtocolChatCompletions, req, nil, maxRetries,\n\t\trequestTimeout, streamIdleTimeout, retryDelay, ch)\n}\n\nfunc createProtocolStreamWithRetry(ctx context.Context, client *openai.Client, protocol string,\n\treq openai.ChatCompletionRequest, responseInput []any, maxRetries int, requestTimeout, streamIdleTimeout time.Duration,\n\tretryDelay func(string, int) time.Duration,\n\tch chan<- AgentEvent) (*util.OpenAICompletionStream, openai.ChatCompletionStreamResponse, context.CancelFunc, error) {\n\tif maxRetries < 0 {\n\t\tmaxRetries = 0\n\t}\n\n\tvar lastErr error","sourceCodeStart":2427,"sourceCodeEnd":2463,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/agent/agent.go#L2427-L2463","documentation":"errModelStreamIdleTimeout is a sentinel error returned when an established model stream produces no data within the configured idle timeout. recvStreamWithIdleTimeout starts a timer around stream.Recv(); if the timer fires first, the stream is cancelled and this sentinel is returned, which classifyRetry treats as a retryable \"timeout\".","triggerScenarios":"stream.Recv() blocks longer than streamIdleTimeout after a partial (or no) response; stopCancelTimer reports the idle timer expired, so recvStreamWithIdleTimeout returns errModelStreamIdleTimeout instead of the blocked recv.","commonSituations":"Provider stalls mid-stream (network hiccup, provider-side overload); streamIdleTimeout set too low for slow token generation gaps; long tool-use pauses from the provider with no keep-alive chunks.","solutions":["Increase the stream idle timeout configuration","Retry — classifyRetry labels this \"timeout\" and it is retried automatically","Check provider status for stream interruptions","Investigate network paths (proxy/VPN) that may stall long-lived connections","Reduce time-to-first-token by shortening the prompt if stalls occur at stream start"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if errors.Is(err, errModelStreamIdleTimeout) {\n    // retryable: back off and reopen the stream, possibly with a larger idle timeout\n}","preventionTips":["Configure streamIdleTimeout above the provider's typical inter-chunk gap","Avoid unstable network paths (proxies/VPNs) for long-lived streams","Verify your retry policy includes the \"timeout\" classification"],"tags":["agent","streaming","timeout","network"],"backgroundTag":"request-timeout","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}