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
- Inspect/log the actual type(raw) in the generator pipeline and fix the upstream producer to return a dict or ConceptGraph
- 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
- Wrap list-shaped payloads (e.g. {'graph': {...}} or [ {...} ]) by extracting the inner dict before validation
- 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
- Parse LLM JSON strings to dicts before passing payloads between stages
- Add contract tests asserting generators return ConceptGraph or dict
- Log type(raw) at stage boundaries to catch shape drift early
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
- animation generation failed: {exc}
- invalid concept_graph payload: {exc}
- LLM returned no deep-dive suggestions.
- LLM did not return any flashcards.
- SectionArchitect produced no subsections in outline pass.
AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27).
Data as JSON: /api/errors/ac4d6b60425b295b.
Report an issue: GitHub.