Skip to content

for await over a variable holding an async generator yields undefined (falls to array desugar) #4405

@TheHypnoo

Description

@TheHypnoo

Summary

for await (const x of gen) where gen is a variable (or the result of a method call) holding an async generator iterates undefined forever instead of the yielded values. Only a direct call to a named async generator function (for await (x of g())) is lowered correctly.

Repro

async function* g() { yield "a"; yield "b"; }
async function main() {
  const gen = g();
  for await (const x of gen) console.log(x); // Node: a, b — Perry: undefined ×∞
}
main();

Root cause

The async generator object has a working .next() returning Promise<{ value, done }>, but no [Symbol.asyncIterator], and the for-await lowering only special-cases direct calls to names in async_generator_func_names. Any other shape — a variable, or an async-generator method result like obj.run() — falls through to the ForOfToArray desugar, which reads .length (undefined) on the generator object.

This blocks @openai/codex-sdk natively: its runStreamedInternal does for await (const item of generator) where generator = this._exec.run({...}) (an async-generator method result).

Notes / direction

  • A compile-time fix must handle the method-call case (this._exec.run()), not just bare functions — needs async-generator return-type tracking through variables and method calls.
  • A general runtime fix (route the for await fallback through an async-iterator protocol) must preserve current correct behaviour for for await over arrays and arrays-of-promises (verified working today), so it needs care to avoid regressions.
  • Installing [Symbol.asyncIterator] on async-generator objects is a correct, isolated sub-step but doesn't fix the loop on its own.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions