{"record":{"id":"85c7298f7164bd36","repo":"siyuan-note/siyuan","slug":"model-returned-nil-stream","errorCode":null,"errorMessage":"model returned nil stream","messagePattern":"model returned nil stream","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/agent/agent.go","lineNumber":2491,"sourceCode":"\t\t\tsendEvent(ch, AgentEvent{Type: \"retry\", RetryAttempt: attempt, RetryMax: maxRetries})\n\t\t}\n\n\t\tstreamCtx, streamCancel := context.WithCancel(ctx)\n\t\trequestTimer, requestTimerDone := startCancelTimer(requestTimeout, streamCancel)\n\t\tstream, err := util.CreateOpenAICompletionStream(streamCtx, client, protocol, req, responseInput)\n\t\trequestTimedOut := stopCancelTimer(requestTimer, requestTimerDone)\n\t\tif ctx.Err() != nil {\n\t\t\tif stream != nil {\n\t\t\t\tstream.Close()\n\t\t\t}\n\t\t\tstreamCancel()\n\t\t\treturn nil, openai.ChatCompletionStreamResponse{}, nil, ctx.Err()\n\t\t}\n\t\tif requestTimedOut {\n\t\t\terr = errModelRequestTimeout\n\t\t}\n\t\tif err == nil && stream == nil {\n\t\t\terr = errors.New(\"model returned nil stream\")\n\t\t}\n\t\tif err == nil {\n\t\t\tfor {\n\t\t\t\tfirstResp, firstErr := recvStreamWithIdleTimeout(stream, streamIdleTimeout, streamCancel)\n\t\t\t\tif firstErr != nil || !util.IsOpenAIResponsesProtocol(protocol) || len(firstResp.Choices) > 0 ||\n\t\t\t\t\tfirstResp.Usage != nil {\n\t\t\t\t\tif firstErr == nil || errors.Is(firstErr, io.EOF) {\n\t\t\t\t\t\treturn stream, firstResp, streamCancel, nil\n\t\t\t\t\t}\n\t\t\t\t\terr = firstErr\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif stream != nil {\n\t\t\tstream.Close()\n\t\t}\n\t\tstreamCancel()","sourceCodeStart":2473,"sourceCodeEnd":2509,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/agent/agent.go#L2473-L2509","documentation":"A defensive invariant check in createStreamWithRetry: if the transport call returned neither an error nor a stream, the agent fabricates this error rather than proceeding with a nil stream. It signals an unexpected/undefined state in the model client response handling.","triggerScenarios":"After the request completes without requestTimedOut and with err == nil, the returned stream is nil (client library returned nothing), so `if err == nil && stream == nil` raises errors.New(\"model returned nil stream\").","commonSituations":"OpenAI client library returning (nil, nil) on unusual transport states; misconfigured protocol/base-URL causing the client to skip stream creation; client library version regressions.","solutions":["Retry the request; a nil stream is often transient client behavior","Check the openai client library version for known nil-stream bugs and upgrade","Verify base URL/protocol configuration points at a real streaming endpoint","Enable transport-level logging to see the raw HTTP response","If reproducible, report the (nil, nil) return to the client library maintainers"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && err.Error() == \"model returned nil stream\" {\n    // retry once; if persistent, log client/version details for diagnosis\n}","preventionTips":["Keep the openai client library up to date","Validate base URL and protocol configuration before deploying","Log raw transport responses when debugging stream setup"],"tags":["agent","streaming","internal","openai"],"backgroundTag":"internal-invariant-violation","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"}