langchain-ai/langchain · error · ValueError

Unexpected generation type

Error message

Unexpected generation type

What it means

`ValueError` raised in the async message-returning helper (used by `apredict`/`ainvoke` internals): after `agenerate` succeeds, `result.generations[0][0]` is not a `ChatGeneration`. The helper expects chat generations whose `.message` it can return; any other `Generation` subclass (plain `Generation`, `ChatGenerationChunk` misuse in custom results) is rejected.

Source

Thrown at libs/core/langchain_core/language_models/chat_models.py:2315

            if item is done:
                break
            yield item  # type: ignore[misc]

    async def _call_async(
        self,
        messages: list[BaseMessage],
        stop: list[str] | None = None,
        callbacks: Callbacks = None,
        **kwargs: Any,
    ) -> BaseMessage:
        result = await self.agenerate(
            [messages], stop=stop, callbacks=callbacks, **kwargs
        )
        generation = result.generations[0][0]
        if isinstance(generation, ChatGeneration):
            return generation.message
        msg = "Unexpected generation type"
        raise ValueError(msg)

    @property
    @abstractmethod
    def _llm_type(self) -> str:
        """Return type of chat model."""

    @deprecated("1.4.2", alternative="asdict", removal="2.0.0")
    @override
    def dict(self, **_kwargs: Any) -> builtins.dict[str, Any]:
        """DEPRECATED - use `asdict()` instead.

        Return a dictionary representation of the chat model.
        """
        return self.asdict()

    def asdict(self) -> builtins.dict[str, Any]:
        """Return a dictionary representation of the chat model."""
        starter_dict = dict(self._identifying_params)

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Wrap messages in `ChatGeneration(message=...)`, not `Generation(text=...)`, inside custom `_agenerate`/`_astream` accumulation.
  2. If you only need text, use the `LLM`/`BaseLLM` hierarchy instead of `BaseChatModel`.
  3. Inspect `type(result.generations[0][0])` in a debugger to confirm which generation class is being produced.
  4. Use `ChatResult(generations=[ChatGeneration(message=AIMessage(...))])` as the canonical return shape.

Example fix

# before
return ChatResult(generations=[[Generation(text="hi")]])

# after
from langchain_core.outputs import ChatGeneration
from langchain_core.messages import AIMessage
return ChatResult(generations=[[ChatGeneration(message=AIMessage(content="hi"))]])
Defensive patterns

Strategy: type-guard

Validate before calling

result = await model.agenerate([messages])
gen = result.generations[0][0]
if not isinstance(gen, ChatGeneration):
    raise TypeError(f"custom model returned {type(gen).__name__}")

Type guard

from langchain_core.outputs import ChatGeneration
def is_chat_generation(g: object) -> bool:
    return isinstance(g, ChatGeneration)

Try / catch

try:
    msg = await model.ainvoke(messages)
except ValueError as e:
    if "Unexpected generation type" in str(e):
        # fix the custom _agenerate to return ChatGeneration, then retry
        raise RuntimeError("custom model must return ChatGeneration") from e
    raise

Prevention

When it happens

Trigger: A custom `BaseChatModel` subclass whose `agenerate`/`_agenerate` builds a `ChatResult` containing non-`ChatGeneration` entries (e.g. plain `Generation(text=...)` copied from an LLM implementation), then the caller uses `ainvoke`/`apredict`-style paths.

Common situations: Porting an `LLM` (completion-style) subclass to `BaseChatModel` and reusing `Generation`; building `ChatResult` manually in test fakes with the wrong generation class; mixing v1 and v2 result shapes.

Related errors


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/2abd0454fe21fca1. Report an issue: GitHub.