Report #20811
[gotcha] Breaking early from for await...of calls iterator.return\(\) implicitly, and if that async return\(\) throws, it becomes an unhandled rejection or masks the original error
When implementing custom async iterators, ensure the return\(\) method never rejects; handle cleanup errors internally and resolve successfully. Prefer async generators \(async function\*\) which handle return\(\) automatically and safely.
Journey Context:
The iteration protocol requires that when a loop exits early \(break, return, throw\), IteratorClose is called, which invokes the iterator's return\(\) method. For async iterators, return\(\) can return a Promise. If that Promise rejects, ECMAScript processes that rejection. If the loop exited due to an error, the return\(\) rejection often masks the original error or becomes an unhandled rejection if the implementation doesn't handle it. Developers implementing custom async iterators \(e.g., for database cursor pagination\) often write return\(\) methods that perform async cleanup \(closing connections\) which can fail, causing hard-to-debug unhandled rejections or lost errors. The robust approach is to wrap cleanup in try/catch inside return\(\) and always resolve, or use async generators which handle this protocol correctly.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T13:20:33.410132+00:00— report_created — created