nodejs/node · error · TypeError

Loop length for some iterators cannot be lazily calculated i

Error message

Loop length for some iterators cannot be lazily calculated in async mode

What it means

Raised by AsyncLoopContext.length (asyncsupport.py) when loop.length / loop.revindex / loop.last and similar length-derived attributes are requested during async iteration but no upfront length was supplied. Unlike sync mode, an async iterator cannot be cheaply len()-ed, so the length is unknown and these attributes are unavailable.

Source

Thrown at tools/inspector_protocol/jinja2/asyncsupport.py:202

            yield item
        return
    for item in iterable:
        yield item


class AsyncLoopContext(LoopContextBase):

    def __init__(self, async_iterator, undefined, after, length, recurse=None,
                 depth0=0):
        LoopContextBase.__init__(self, undefined, recurse, depth0)
        self._async_iterator = async_iterator
        self._after = after
        self._length = length

    @property
    def length(self):
        if self._length is None:
            raise TypeError('Loop length for some iterators cannot be '
                            'lazily calculated in async mode')
        return self._length

    def __aiter__(self):
        return AsyncLoopContextIterator(self)


class AsyncLoopContextIterator(object):
    __slots__ = ('context',)

    def __init__(self, context):
        self.context = context

    def __aiter__(self):
        return self

    async def __anext__(self):
        ctx = self.context

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Materialize the async iterator into a list before the loop so the length is known: `{% for x in await to_list(source) %}`.
  2. Pre-compute the length and pass it explicitly if the loop construct supports a length argument.
  3. Rewrite the template body to not depend on total count (use loop.index / loop.index0 only, which need no length).

Example fix

{# before #}
{% for x in async_source %}{{ loop.length }}{% endfor %}
{# after #}
{% set items = async_source | list %}
{% for x in items %}{{ items | length }}{% endfor %}
Defensive patterns

Strategy: validation

Validate before calling

async def materialize(ait):
    out = []
    async for x in ait:
        out.append(x)
    return out

# in template: {% set items = await to_list(source) %} then iterate `items`

Prevention

When it happens

Trigger: In an async-enabled template, iterating `{% for x in some_async_gen %}` and inside the loop body referencing `{{ loop.length }}`, `{{ loop.revindex }}`, `{{ loop.last }}`, or `{{ loop.first }}`-style helpers that need the total count.

Common situations: Iterating an async generator / async DB cursor in a template and trying to show 'item N of M' progress; reusing a sync template that used loop.length against a now-async data source.

Related errors


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