HKUDS/DeepTutor · error · GenerationFailure

concept_graph payload missing from BlockContext.extra

Error message

concept_graph payload missing from BlockContext.extra

What it means

The concept_graph block needs a ConceptGraph payload and looks it up in `ctx.extra['concept_graph']` then `ctx.block.params['concept_graph']`; when neither is present it fails generation with this message.

Source

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

    arrow_for = {"depends_on": "-->", "extends": "==>", "related": "-.->"}
    for edge in graph.edges:
        if edge.src not in id_map or edge.dst not in id_map:
            continue
        arrow = arrow_for.get(edge.relation, "-->")
        lines.append(f"  {id_map[edge.src]} {arrow} {id_map[edge.dst]}")

    return "\n".join(lines)


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] = {}

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Pass the ConceptGraph when building the context: put it in extra under 'concept_graph' or into block params
  2. If driving the standard compiler, ensure the concept-graph extraction stage runs before block generation
  3. If a one-off render, construct `ConceptGraph(...)` and inject it explicitly

Example fix

# before
ctx = BlockContext(block=block, language="en", extra={})

# after
ctx = BlockContext(
    block=block,
    language="en",
    extra={"concept_graph": my_concept_graph},
)
Defensive patterns

Strategy: validation

Validate before calling

if "concept_graph" not in ctx.extra and "concept_graph" not in (ctx.block.params or {}):
    raise ValueError("inject concept_graph before generating concept_graph blocks")

Type guard

def has_graph_payload(ctx) -> bool:
    return ctx.extra.get("concept_graph") is not None or \
           ctx.block.params.get("concept_graph") is not None

Try / catch

from deeptutor.book.compiler import GenerationFailure
try:
    await cg_gen._generate(ctx)
except GenerationFailure as e:
    if "payload missing" in str(e):
        ctx.extra["concept_graph"] = build_graph(chapters)
        return await cg_gen._generate(ctx)
    raise

Prevention

When it happens

Trigger: Compiling a book whose outline contains a `concept_graph` block while the caller (pipeline/compiler) forgot to inject the graph into BlockContext.extra, and the block params don't carry it either — e.g. custom pipelines constructing BlockContext by hand.

Common situations: Building your own generation pipeline and passing a bare BlockContext; reordering compiler stages so graph extraction runs after block generation; serializing/deserializing contexts and dropping `extra`.

Related errors


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