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, done

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Retry the AI editor request once to rule out a transient internal state
  2. Try switching the provider protocol setting (openai vs openai-responses) to exercise the other code path
  3. 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

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


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/72c3bbe037588767. Report an issue: GitHub.