siyuan-note/siyuan · error
AI editor model returned nil stream
Error message
AI editor model returned nil stream
What it means
After util.CreateOpenAICompletionStream returns without error, NewAIEditorChatStream (kernel/model/ai.go:201) defensively checks that the stream pointer is non-nil. Reading kernel/util/openai_completion.go:51 shows both protocol branches (chat completions and responses) always return a non-nil wrapper or a non-nil error, so a nil stream with a nil error cannot occur in the current code — this is an invariant guard against future regressions or internal state corruption.
Source
Thrown at kernel/model/ai.go:201
Messages: messages,
Stream: true,
}
streamCtx, cancel := context.WithCancel(ctx)
requestTimeout := time.Duration(prov.RequestTimeout) * time.Second
requestTimer, requestTimerDone := startAIEditorCancelTimer(requestTimeout, cancel)
client := util.NewOpenAIClientWithModel(prov.APIKey, prov.BaseURL, m.Name)
completionStream, err := util.CreateOpenAICompletionStream(streamCtx, client, prov.Protocol, req, nil)
requestTimedOut := stopAIEditorCancelTimer(requestTimer, requestTimerDone)
if requestTimedOut {
err = errors.New("AI editor request timeout")
}
if nil != err {
cancel()
return nil, err
}
if nil == completionStream {
cancel()
return nil, errors.New("AI editor model returned nil stream")
}
return &AIEditorChatStream{
stream: completionStream,
cancel: cancel,
idleTimeout: 120 * time.Second,
}, nil
}
func startAIEditorCancelTimer(timeout time.Duration, cancel context.CancelFunc) (*time.Timer, <-chan struct{}) {
if 0 >= timeout {
return nil, nil
}
done := make(chan struct{})
timer := time.AfterFunc(timeout, func() {
cancel()
close(done)
})
return timer, doneView on GitHub (pinned to afa823b6b4)
Solutions
- Retry the AI editor request once to rule out a transient internal state
- Try switching the provider protocol setting (openai vs openai-responses) to exercise the other code path
- Report it upstream as a kernel bug, attaching the kernel log, provider protocol, and SiYuan version
Defensive patterns
Strategy: try-catch
Try / catch
if err != nil && err.Error() == "AI editor model returned nil stream" {
// internal invariant break: retry once, then collect diagnostics
logKernelVersionAndProviderProtocol()
return retryOrFail(err)
} Prevention
- Run release kernel builds; invariant guards like this target regressions, not user input
- Keep provider protocol settings on supported values (openai, openai-responses)
- Report recurrences upstream with the kernel log
When it happens
Trigger: Not reachable through normal configuration: would require CreateOpenAICompletionStream to return (nil, nil), which no current code path does. If it ever appears, it indicates a kernel bug (e.g. a new protocol branch forgetting to construct the wrapper) rather than a user mistake.
Common situations: Running a modified or nightly kernel build where a protocol branch was changed; essentially never seen on release builds.
Related errors
- block write failed: empty block ID
- export failed: empty artifact path
- AI editor stream idle timeout
- no AI provider configured
- AI editor request timeout
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/72c3bbe037588767.
Report an issue: GitHub.