microsoft/autogen · error · ValueError

Unexpected finish reason: {choice.finish_reason}

Error message

Unexpected finish reason: {choice.finish_reason}

What it means

Raised while consuming a create_stream() response in the Azure AI client: when a streaming chunk carries a finish_reason that is not a CompletionsFinishReason enum and is not one of the strings 'stop', 'length', 'function_calls', 'content_filter', 'unknown', the client raises ValueError('Unexpected finish reason: ...'). This is the fallback branch of the finish-reason mapping — the service reported a terminal state this client version cannot interpret.

Source

Thrown at python/packages/autogen-ext/src/autogen_ext/models/azure/_azure_ai_client.py:533

                # Emit the start event.
                logger.info(
                    LLMStreamStartEvent(
                        messages=[m.as_dict() for m in azure_messages],
                    )
                )
            assert isinstance(chunk, StreamingChatCompletionsUpdate)
            choice = chunk.choices[0] if len(chunk.choices) > 0 else None
            if choice and choice.finish_reason is not None:
                if isinstance(choice.finish_reason, CompletionsFinishReason):
                    finish_reason = cast(FinishReasons, choice.finish_reason.value)
                    # Handle special case for TOOL_CALLS finish reason
                    if choice.finish_reason is CompletionsFinishReason.TOOL_CALLS:
                        finish_reason = "function_calls"
                else:
                    if choice.finish_reason in ["stop", "length", "function_calls", "content_filter", "unknown"]:
                        finish_reason = choice.finish_reason  # type: ignore
                    else:
                        raise ValueError(f"Unexpected finish reason: {choice.finish_reason}")

            # We first try to load the content
            if choice and choice.delta.content is not None:
                content_deltas.append(choice.delta.content)
                yield choice.delta.content
            # Otherwise, we try to load the tool calls
            if choice and choice.delta.tool_calls is not None:
                for tool_call_chunk in choice.delta.tool_calls:
                    # print(tool_call_chunk)
                    if "index" in tool_call_chunk:
                        idx = tool_call_chunk["index"]
                    else:
                        idx = tool_call_chunk.id
                    if idx not in full_tool_calls:
                        full_tool_calls[idx] = FunctionCall(id="", arguments="", name="")

                    full_tool_calls[idx].id += tool_call_chunk.id
                    full_tool_calls[idx].name += tool_call_chunk.function.name

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Upgrade autogen-ext to a release mapping the new finish reason
  2. Point the client at the genuine Azure AI Foundry / GitHub Models endpoint rather than an OpenAI-compatible proxy
  3. If stuck on this version, catch the ValueError around stream consumption and retry without streaming (create() uses a different code path)

Example fix

# before
async for chunk in client.create_stream(msgs):
    print(chunk)

# after (defensive: fall back to non-streaming on unknown finish reason)
try:
    async for chunk in client.create_stream(msgs):
        print(chunk)
except ValueError as e:
    if "Unexpected finish reason" in str(e):
        result = await client.create(msgs)
    else:
        raise
Defensive patterns

Strategy: try-catch

Try / catch

try:
    async for chunk in client.create_stream(msgs):
        process(chunk)
except ValueError as e:
    if "Unexpected finish reason" in str(e):
        result = await client.create(msgs)  # non-streaming fallback
    else:
        raise

Prevention

When it happens

Trigger: Azure AI Foundry / GitHub Models starts returning a new finish_reason value (string) that predates support in the installed autogen-ext; a proxy or gateway (custom endpoint) emits non-standard finish reasons like 'tool_calls' (instead of the mapped 'function_calls').

Common situations: Version skew: service-side rollout of new stop reasons (e.g. reasoning-model reasons) before autogen-ext update; routing the client through an OpenAI-compatible proxy that uses OpenAI vocabularly ('tool_calls') instead of the Azure inference SDK enum.

Related errors


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/54f3ed89923e28ef. Report an issue: GitHub.