microsoft/semantic-kernel · error · ServiceInvalidResponseError

Expected an AsyncStream[ChatCompletionChunk] response.

Error message

Expected an AsyncStream[ChatCompletionChunk] response.

What it means

Raised inside the streaming chat-completion generator (_complete_chat_stream) after the OpenAI client returns a response object that is not an openai.AsyncStream. The method unconditionally sets stream=True on the settings and expects the SDK to yield ChatCompletionChunk objects; a non-stream response indicates the request was misconfigured or the SDK version is incompatible.

Source

Thrown at python/semantic_kernel/connectors/ai/open_ai/services/open_ai_chat_completion_base.py:112

    @trace_streaming_chat_completion(MODEL_PROVIDER_NAME)
    async def _inner_get_streaming_chat_message_contents(
        self,
        chat_history: "ChatHistory",
        settings: "PromptExecutionSettings",
        function_invoke_attempt: int = 0,
    ) -> AsyncGenerator[list["StreamingChatMessageContent"], Any]:
        if not isinstance(settings, OpenAIChatPromptExecutionSettings):
            settings = self.get_prompt_execution_settings_from_settings(settings)
        assert isinstance(settings, OpenAIChatPromptExecutionSettings)  # nosec

        settings.stream = True
        settings.stream_options = {"include_usage": True}
        settings.messages = self._prepare_chat_history_for_request(chat_history)
        settings.ai_model_id = settings.ai_model_id or self.ai_model_id

        response = await self._send_request(settings)
        if not isinstance(response, AsyncStream):
            raise ServiceInvalidResponseError("Expected an AsyncStream[ChatCompletionChunk] response.")
        async for chunk in response:
            if len(chunk.choices) == 0 and chunk.usage is None:
                continue

            assert isinstance(chunk, ChatCompletionChunk)  # nosec
            chunk_metadata = self._get_metadata_from_streaming_chat_response(chunk)
            if (not chunk.choices or len(chunk.choices) == 0) and chunk.usage is not None:
                # Usage is contained in the last chunk where the choices are empty
                # We are duplicating the usage metadata to all the choices in the response
                yield [
                    StreamingChatMessageContent(
                        role=AuthorRole.ASSISTANT,
                        content="",
                        choice_index=i,
                        inner_content=chunk,
                        ai_model_id=settings.ai_model_id,
                        metadata=chunk_metadata,
                        function_invoke_attempt=function_invoke_attempt,

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Ensure settings.stream is not being overridden to False before or after the streaming call path is entered
  2. Verify the openai package version matches the one required by your semantic-kernel version (check pyproject.toml or requirements.txt)
  3. If using a custom client, confirm it returns an AsyncStream[ChatCompletionChunk] when stream=True is passed
  4. Avoid injecting a pre-configured client that wraps or intercepts the create() call in a way that changes the return type

Example fix

# before — mock returns a non-stream object
mock_client.chat.completions.create.return_value = ChatCompletion(...)
# after — mock returns an AsyncStream
async def _fake_stream():
    yield ChatCompletionChunk(...)
mock_client.chat.completions.create.return_value = _fake_stream()
Defensive patterns

Strategy: try-catch

Type guard

from openai import AsyncStream
from openai.types.chat import ChatCompletionChunk

def is_valid_stream_response(response) -> bool:
    return isinstance(response, AsyncStream)

Try / catch

from semantic_kernel.exceptions.service_exceptions import ServiceInvalidResponseError

try:
    async for chunk in service._complete_chat_stream(...):
        ...
except ServiceInvalidResponseError as e:
    logger.error('Streaming response was not an AsyncStream; falling back to non-streaming')
    response = await service.get_chat_message_content(...)

Prevention

When it happens

Trigger: Calling the streaming completion path (get_streaming_chat_message_content or _complete_chat_stream) when the underlying OpenAI SDK client returns a non-streamed ChatCompletion object — e.g., because stream was overridden elsewhere, a custom client wrapper stripped streaming, or the openai package version returns a different response type.

Common situations: Using a mocked or custom AsyncOpenAI client in tests that returns a ChatCompletion instead of an AsyncStream; upgrading/downgrading the openai Python package to a version with a changed streaming contract; a proxy or gateway that buffers and de-chunks the response.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/b9039648f816ce47. Report an issue: GitHub.