Skip to content

Bug: Streaming chat crashes with 'Unsupported renderer' and cancel scope error #423

@lbedner

Description

@lbedner

Bug Description

Streaming chat crashes with two linked errors:

  1. Marko markdown rendering fails with "Unsupported renderer"
  2. Async cleanup crashes with "Attempted to exit cancel scope in a different task"

Error Messages

Streaming error: Unsupported renderer <class 'marko._Renderer'>
Error: Unsupported renderer <class 'marko._Renderer'>

[error] Streaming failed: Attempted to exit cancel scope in a different task than it was entered in

Followed by full crash with asyncio.exceptions.CancelledError.

Steps to Reproduce

  1. Start interactive chat: my-app ai chat
  2. Switch to a model like gpt-5-mini: /model gpt-5-mini
  3. Send a message that generates a response with certain markdown elements
  4. Chat crashes with the above errors

Root Cause Analysis

Issue 1: Marko Renderer

The custom TerminalRenderer in app/cli/marko_terminal_renderer.py may not have handlers for all markdown element types. When marko encounters an element without a matching render_* method, it falls back to the default _Renderer which throws the error.

Likely missing renderers for:

  • Certain GFM elements (task lists, strikethrough, etc.)
  • Edge case elements the AI generated

Issue 2: Cancel Scope Crossing

When the marko error occurs mid-stream:

  • The streaming async generator is interrupted
  • pydantic_ai's streaming cleanup runs via cancel_and_wait_for_background_tasks()
  • The cancel scope was entered in one task but exits in another
  • This is a known anyio/pydantic_ai interaction issue

Files Involved

  • app/cli/marko_terminal_renderer.py - Custom renderer (needs more element handlers)
  • app/cli/ai_rendering.py - Streaming renderer
  • app/services/ai/service.py - stream_chat() method
  • app/cli/ai.py - Interactive chat loop

Suggested Fix

  1. Add fallback handling in TerminalRenderer - Catch unknown elements and render as plain text instead of crashing
  2. Wrap streaming in try/except - Gracefully handle rendering errors without crashing the whole session
  3. Consider anyio compatibility - May need to ensure cancel scopes don't cross task boundaries

Context

Error occurred after switching models mid-session. The AI response contained markdown that triggered the renderer issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions