nodejs/node · error · RuntimeError

Async mode requires a body stream to be passed to a template

Error message

Async mode requires a body stream to be passed to a template module.  Use the async methods of the API you are using.

What it means

TemplateModule wraps an imported template (created via {% import %} or Environment.get_template().module). Its constructor must materialize the rendered output: normally it does list(template.root_render_func(context)), but in an async environment root_render_func is an async generator that cannot be drained synchronously. Therefore Jinja2 refuses to construct the module without a pre-collected body_stream.

Source

Thrown at tools/inspector_protocol/jinja2/environment.py:1148

    def __repr__(self):
        if self.name is None:
            name = 'memory:%x' % id(self)
        else:
            name = repr(self.name)
        return '<%s %s>' % (self.__class__.__name__, name)


@implements_to_string
class TemplateModule(object):
    """Represents an imported template.  All the exported names of the
    template are available as attributes on this object.  Additionally
    converting it into an unicode- or bytestrings renders the contents.
    """

    def __init__(self, template, context, body_stream=None):
        if body_stream is None:
            if context.environment.is_async:
                raise RuntimeError('Async mode requires a body stream '
                                   'to be passed to a template module.  Use '
                                   'the async methods of the API you are '
                                   'using.')
            body_stream = list(template.root_render_func(context))
        self._body_stream = body_stream
        self.__dict__.update(context.get_exported())
        self.__name__ = template.name

    def __html__(self):
        return Markup(concat(self._body_stream))

    def __str__(self):
        return concat(self._body_stream)

    def __repr__(self):
        if self.__name__ is None:
            name = 'memory:%x' % id(self)
        else:

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Render via the async API so Jinja2 collects the body stream for you: use await template.render_async() / async for chunk in template.generate_async(), and access the imported module through await template.make_module_async() (or context_vars) instead of the synchronous .module property.
  2. If you only need macros, inline them or pass rendered strings rather than importing a module under enable_async.
  3. If async is not actually required, construct the Environment without enable_async (the default) so the synchronous module path works.
  4. Bump/verify your Jinja2 version supports the async module API (make_module_async / generate_async); older vendored copies may lack it.

Example fix

// before
env = Environment(loader=..., enable_async=True)
tmpl = env.get_template('page.html')
mod = tmpl.module  # raises: async mode requires a body stream

// after
env = Environment(loader=..., enable_async=True)
tmpl = env.get_template('page.html')
mod = await tmpl.make_module_async()  # body stream collected asynchronously
Defensive patterns

Strategy: validation

Validate before calling

from jinja2 import Environment

def safe_module(env, name):
    tmpl = env.get_template(name)
    if env.is_async:
        raise RuntimeError(
            'cannot access .module synchronously under enable_async; '
            'await tmpl.make_module_async() instead')
    return tmpl.module

Type guard

def supports_sync_module(env: Environment) -> bool:
    return not getattr(env, 'is_async', False)

Try / catch

try:
    mod = tmpl.module
except RuntimeError as e:
    if 'Async mode' in str(e):
        raise RuntimeError('await tmpl.make_module_async() under enable_async') from e
    raise

Prevention

When it happens

Trigger: Constructing a TemplateModule synchronously (or calling .module on a template) while Environment(enable_async=True) is set and no body_stream was pre-collected via the async render path. Typically hit when mixing {% import 'macros.html' as m %} with an async environment, or calling template.module instead of await template.make_module_async().

Common situations: Adopting async rendering under asyncio/Starlette/Sanic/aiohttp and reusing existing {% import %} macros; upgrading a sync Jinja2 codebase to enable_async=True without converting the import/module access sites.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/6a6ac33c7b4c4811. Report an issue: GitHub.