HKUDS/DeepTutor · error · GenerationFailure

unexpected concept_graph payload type: {type(raw).__name__}

Error message

unexpected concept_graph payload type: {type(raw).__name__}

What it means

This GenerationFailure is thrown by the concept_graph book block when the raw payload returned by the LLM/generator is neither a ConceptGraph instance (or dict) — i.e. model_validate cannot be applied — because the object is of some unexpected type such as a list, str, or None. It signals that the generator contract (return a ConceptGraph model or a validate-able dict) was violated upstream.

Source

Thrown at deeptutor/book/blocks/concept_graph.py:100

class ConceptGraphGenerator(BlockGenerator):
    block_type = BlockType.CONCEPT_GRAPH

    async def _generate(
        self, ctx: BlockContext
    ) -> tuple[dict[str, Any], list[SourceAnchor], dict[str, Any]]:
        raw = ctx.extra.get("concept_graph") or ctx.block.params.get("concept_graph")
        if raw is None:
            raise GenerationFailure("concept_graph payload missing from BlockContext.extra")
        if isinstance(raw, ConceptGraph):
            graph = raw
        elif isinstance(raw, dict):
            try:
                graph = ConceptGraph.model_validate(raw)
            except Exception as exc:
                raise GenerationFailure(f"invalid concept_graph payload: {exc}") from exc
        else:
            raise GenerationFailure(f"unexpected concept_graph payload type: {type(raw).__name__}")

        chapters_index = ctx.extra.get("chapter_index") or []
        if not isinstance(chapters_index, list):
            chapters_index = []

        mermaid_src = render_mermaid(graph)

        # Build a node→chapter lookup for the interactive sidebar.
        node_to_chapter: dict[str, str] = {}
        for n in graph.nodes:
            if n.chapter_id:
                node_to_chapter[n.id] = n.chapter_id

        return (
            {
                "render_type": "concept_graph",
                "code": {"language": "mermaid", "content": mermaid_src},
                "graph": graph.model_dump(),

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Inspect/log the actual type(raw) in the generator pipeline and fix the upstream producer to return a dict or ConceptGraph
  2. If the LLM returns a JSON string, parse it with json.loads (or the project's LLM JSON extractor) before handing it to the block
  3. Wrap list-shaped payloads (e.g. {'graph': {...}} or [ {...} ]) by extracting the inner dict before validation
  4. Upgrade/degrade the generator package to a version whose return contract matches the block's expectations

Example fix

// before
raw = await generator.generate(...)  # returns '{"nodes": [...]}' as str

// after
import json
if isinstance(raw, str):
    raw = json.loads(raw)
if isinstance(raw, list) and raw and isinstance(raw[0], dict):
    raw = raw[0]
Defensive patterns

Strategy: validation

Validate before calling

def is_concept_graph_payload(raw) -> bool:
    return isinstance(raw, dict) and 'nodes' in raw

Type guard

from deeptutor.book.blocks.concept_graph import ConceptGraph

def is_concept_graph_payload(raw) -> bool:
    if isinstance(raw, ConceptGraph):
        return True
    if not isinstance(raw, dict):
        return False
    try:
        ConceptGraph.model_validate(raw)
        return True
    except Exception:
        return False

Try / catch

try:
    block.generate(ctx)
except GenerationFailure as exc:
    if 'unexpected concept_graph payload type' in str(exc):
        raw = inspect_upstream_generator_output()
        # parse str/list wrappers then retry once

Prevention

When it happens

Trigger: Calling the concept_graph block's _generate when the generator/pipeline returns raw=None, a JSON string, a list of nodes, or any object that is not a ConceptGraph pydantic model and not a dict. Typically happens when an LLM returns a bare JSON string that wasn't parsed, or a tool returns a list wrapper like {'graphs': [...]}.

Common situations: LLM output parsing step skipped (raw stays a str); upstream generator changed its return type after a refactor or version bump; mocked/test generators returning fixtures with the wrong shape; None returned when the generator silently fails.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/ac4d6b60425b295b. Report an issue: GitHub.