You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
break label out of a switch in an async generator throws TypeError reading 'done' #9199
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'done')
The done read failing points at the async-iterator protocol: the labeled break appears to leave the generator state machine without producing a well-formed iterator result, so for await reads .done off undefined. The plain break in case 1 is fine — it is the labeled one that targets the enclosing for…of that fails.
Confirmed pre-existing on main by reverting crates/perry-transform/src/generator/linearize.rs to main and rebuilding: identical error, exit 1 both ways. Found while auditing #9189; that PR fixes the labeled-switch case (#9186) and this async-generator shape is not in its scope.
Sibling: the async function form with a labeled for…of hangs rather than throwing, filed separately.
Taking this. I'll reproduce against current main (which already carries #9189), work out why the labeled break leaves the async-generator state machine without a well-formed iterator result, add a gap regression, and open a PR.
Re-measured against current main (28c292517, which carries #9189). Two things changed since this was filed, and the second is why the issue should stay open.
This issue's own repro now passes. The one-level shape — break loop from a switch inside a single labeled for…of — prints y1,d3, matching node. #9189 covered it after all, despite the note here that it was out of scope.
The defect it describes is still live, one level deeper, with the identical error. The switch turns out to be a red herring; the trigger is a labeled break/continue that targets an outer loop from inside a nested loop, after a yield. Three isolated programs, each on its own:
// p6 — break an outer label from the inner loopasyncfunction*p6(){ O: for(constxof[1,2]){ I: for(constyof[0,1]){yield"p6"+x+y;break O;}}}// node: p610 perry: TypeError: Cannot read properties of undefined (reading 'done')// p5 — continue an outer label from the inner loopasyncfunction*p5(){ O: for(constxof[1,2]){ I: for(constyof[0,1]){yield"p5"+x+y;continue O;}}}// node: p510,p520 perry: NO OUTPUT AT ALL — the `for await` never yields and never settles// pD — this issue's shape with the switch deletedasyncfunction*d(){ outer: for(constxof[1,2]){ inner: for(constyof[0,1]){if(x+y===3)break outer;if(x+y===1){yield"D"+x+y;continue inner;}}}}// node: D10 perry: TypeError: Cannot read properties of undefined (reading 'done')
p6's error is character-for-character the one reported here, so this is the same defect, not a new one.
What still works, which brackets it tightly:
shape
result
labeled continue on the same loop, after a yield
OK
labeled continue of the inner label from the inner loop
OK
unlabeled continue after a yield
OK
labeled continue over a while
OK
labeled break/continue in a sync generator, same shape
OK
break outer from a switch in the inner loop, when nothing yields in the inner loop
OK
break/continue of an OUTER label from a nested loop, after a yield
broken
So the surviving hole is specifically the cross-loop target: one linearized loop's label resolved from inside another linearized loop, where the inner loop has a suspension point. continue and break fail differently (silent drop vs. malformed iterator result), which suggests the target resumption state is wrong rather than absent.
Also worth noting for whoever picks this up: the sibling async function hang mentioned at the bottom of this issue does not reproduce — asyncfn-break-label returns f1,f3, matching node.
I'm working on this now; probes are on the build host under /root/cc9575/ts/iso_p5.ts, iso_p6.ts, iso_pD.ts.
Activity
proggeramlug commentedon Sep 4, 2026
Taking this. I'll reproduce against current main (which already carries #9189), work out why the labeled break leaves the async-generator state machine without a well-formed iterator result, add a gap regression, and open a PR.
proggeramlug commentedon Sep 4, 2026
Re-measured against current
main(28c292517, which carries #9189). Two things changed since this was filed, and the second is why the issue should stay open.This issue's own repro now passes. The one-level shape —
break loopfrom aswitchinside a single labeledfor…of— printsy1,d3, matching node. #9189 covered it after all, despite the note here that it was out of scope.The defect it describes is still live, one level deeper, with the identical error. The switch turns out to be a red herring; the trigger is a labeled
break/continuethat targets an outer loop from inside a nested loop, after ayield. Three isolated programs, each on its own:p6's error is character-for-character the one reported here, so this is the same defect, not a new one.What still works, which brackets it tightly:
continueon the same loop, after a yieldcontinueof the inner label from the inner loopcontinueafter a yieldcontinueover awhilebreak outerfrom aswitchin the inner loop, when nothing yields in the inner loopbreak/continueof an OUTER label from a nested loop, after ayieldSo the surviving hole is specifically the cross-loop target: one linearized loop's label resolved from inside another linearized loop, where the inner loop has a suspension point.
continueandbreakfail differently (silent drop vs. malformed iterator result), which suggests the target resumption state is wrong rather than absent.Also worth noting for whoever picks this up: the sibling
async functionhang mentioned at the bottom of this issue does not reproduce —asyncfn-break-labelreturnsf1,f3, matching node.I'm working on this now; probes are on the build host under
/root/cc9575/ts/iso_p5.ts,iso_p6.ts,iso_pD.ts.fix(transform): unwind a labeled escape out of nested loops