HKUDS/DeepTutor · error · GenerationFailure

SectionArchitect produced no subsections in outline pass.

Error message

SectionArchitect produced no subsections in outline pass.

What it means

The section block runs a two-pass flow: SectionArchitect first produces an outline dict, and pass 2 fills subsections. If outline.get('subsections') is empty/missing, the outline pass failed to deliver structure and GenerationFailure is raised before any content generation.

Solutions

  1. Log the raw outline payload to see whether subsections is absent, empty, or under another key
  2. Retry — outline omissions are often transient
  3. Tighten the architect prompt to enforce the exact JSON schema with a minimum subsection count
  4. Use a stronger model and ensure focus_topic/target_words/section_role inputs are sane
Defensive patterns

Strategy: retry

Type guard

def outline_has_subsections(outline) -> bool:
    return isinstance(outline, dict) and isinstance(outline.get('subsections'), list) and len(outline['subsections']) > 0

Try / catch

try:
    return await section_block.generate(ctx)
except GenerationFailure as exc:
    if 'no subsections' in str(exc):
        return await section_block.generate(ctx)  # outline passes are flaky
    raise

Prevention

When it happens

Trigger: Calling section _generate when SectionArchitect returns an outline without a 'subsections' key, with an empty list, or a payload where subsections parses to nothing (e.g. outline is a refusal string parsed to {}).

Common situations: LLM returns JSON missing 'subsections' due to prompt/schema drift; subsections under a different key ('sections'); overly constrained target_words or odd section_role confusing the architect; model too weak to follow the outline schema; rag_context empty leading to degenerate outline.

Related errors


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

Appendix: source

Thrown at deeptutor/book/blocks/section.py:103

        # when the cached sweep returned nothing.
        rag = await optional_rag_lookup(
            query=f"{chapter_title}: {focus_topic}",
            ctx=ctx,
        )

        # ── Pass 1: outline ───────────────────────────────────────────
        outline = await self._make_outline(
            ctx=ctx,
            chapter_title=chapter_title,
            chapter_summary=chapter_summary,
            objectives=objectives,
            focus_topic=focus_topic,
            section_role=section_role,
            target_words=target_words,
            rag_context=rag.text,
        )
        if not outline.get("subsections"):
            raise GenerationFailure("SectionArchitect produced no subsections in outline pass.")

        # ── Pass 2: fill subsections in parallel ─────────────────────
        relevant_chunks: list[SourceChunk] = []
        try:
            relevant_chunks = ctx.relevant_chunks(focus_topic, limit=8)
        except Exception:  # noqa: BLE001
            relevant_chunks = []

        subs = outline["subsections"]
        coros = [
            self._fill_subsection(
                ctx=ctx,
                chapter_title=chapter_title,
                section_focus=focus_topic,
                outline_intro=outline.get("intro", ""),
                sub=sub,
                chunks=relevant_chunks,
            )

View on GitHub (pinned to 3e82f13042)