Skip to content

Treat package:stack_trace async-gap marker as asynchronous suspension#185791

Merged
auto-submit[bot] merged 4 commits into
flutter:masterfrom
gabrimatic:stack-frame-parallel-wait-async-gap
Jul 21, 2026
Merged

Treat package:stack_trace async-gap marker as asynchronous suspension#185791
auto-submit[bot] merged 4 commits into
flutter:masterfrom
gabrimatic:stack-frame-parallel-wait-async-gap

Conversation

@gabrimatic

Copy link
Copy Markdown
Contributor

Summary

StackFrame.fromStackTraceLine asserted whenever it encountered the literal

===== asynchronous gap ===========================

line that package:stack_trace emits between chained Dart-VM stacks. The assertion's diagnostic told callers to wire up FlutterError.demangleStackTrace, but utilities like debugPrintStack (and, transitively, FlutterError.defaultStackFilter) run the parser unconditionally on caller-supplied stacks. Anything that carries that marker — e.g. the stack inside ParallelWaitError, or any stack that passes through the test runner before reaching the framework — crashes before the demangle hook can intervene. The user-facing repro in the linked issue shows this with (future1, future2).wait's onError handler.

That marker is the package:stack_trace equivalent of the VM's <asynchronous suspension> line. This change folds it into StackFrame.asynchronousSuspension so the boundary is preserved and parsers downstream of fromStackTraceLine (the default stack filter, debugPrintStack, etc.) round-trip cleanly without losing the async hop.

Changes

  • packages/flutter/lib/src/foundation/stack_frame.dart — recognize the package:stack_trace async-gap line as asynchronousSuspension; remove the assertion that previously rejected it.
  • packages/flutter/test/foundation/stack_frame_test.dart — replace the legacy "throws assertion" test with a parse-correctness test, add a unit test for the marker, and add an end-to-end regression test that pipes a stack carrying the marker through debugPrintStack (the exact code path from the linked issue).

Test plan

  • flutter test packages/flutter/test/foundation/stack_frame_test.dart — 13/13 pass.
  • flutter test packages/flutter/test/foundation/ — 222/222 pass.
  • Reproduced the linked issue's failing test ((f1(), f2()).wait with debugPrintStack in onError) and verified it now passes.
  • flutter analyze on the changed files — clean.

Fixes #179018

@github-actions github-actions Bot added the framework flutter/packages/flutter repository. See also f: labels. label Apr 29, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modifies StackFrame.fromStackTraceLine to treat the package:stack_trace asynchronous gap marker as an asynchronous suspension, removing a previous assertion that caused crashes. Test cases were updated to reflect this change and ensure debugPrintStack handles these markers correctly. Review feedback suggests correcting a typo in a code comment and removing an unused async keyword in the test suite.

Comment thread packages/flutter/test/foundation/stack_frame_test.dart Outdated
Comment thread packages/flutter/test/foundation/stack_frame_test.dart Outdated
@gabrimatic
gabrimatic force-pushed the stack-frame-parallel-wait-async-gap branch 5 times, most recently from ebf63a9 to 1e7aa80 Compare May 2, 2026 09:07

@justinmc justinmc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little scary given that a change this deep is required to fix a problem with debugPrintStack. How do we know that this won't break anything else? I assume that assert was there for a reason. I'm not super familiar with StackFrame though.

@gabrimatic

gabrimatic commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, that's a fair concern. I dug into why the assert was there and added focused test-only follow-ups (8dfc81b, then 46e6d69 to make the output capture web-safe).

My read is that the original assert was intended to flag a package:stack_trace chain boundary reaching this parser. One subtlety is that it was not a broad guard against every package:stack_trace-formatted frame: it only fired on the separator line. Individual friendly stack-frame lines that do not start with # already went through the existing non-VM parsing path. So this PR does not broaden how ordinary frames are parsed; it only gives the known chain boundary line an explicit meaning.

I also checked the package:stack_trace source used by the repo: this string is its chainGap separator, and Chain.toString() joins traces with exactly this line:

===== asynchronous gap ===========================

That line is semantically the same kind of boundary as the VM's existing async marker:

<asynchronous suspension>

Before this PR, debug mode asserted on that exact line. In profile/release, where the assert is absent, the parser would already drop the line as unparsed, so the practical behavior was inconsistent: debug crashed, non-debug silently lost the async boundary. This PR narrows that to treating only the exact sentinel as StackFrame.asynchronousSuspension.

The follow-up tests now cover:

  • the actual ParallelWaitError path from the issue, including checking that the captured stack contains the async-gap sentinel and that debugPrintStack does not throw
  • near-miss strings, to show similar-looking lines still parse to null
  • the existing Trace / Chain demangling path in flutter_test, to make sure the normal binding demangle path is still intact
  • VM and Chrome output capture, since web routes debugPrintStack through ErrorToConsoleDumper instead of the debugPrint callback

Verification I ran:

  • bin/cache/dart-sdk/bin/dart --enable-asserts dev/tools/bin/format.dart packages/flutter/test/foundation/stack_frame_test.dart
  • ./bin/flutter analyze --current-package --no-pub packages/flutter/lib/src/foundation/stack_frame.dart packages/flutter/test/foundation/stack_frame_test.dart
  • ./bin/flutter test packages/flutter/test/foundation/stack_frame_test.dart
  • cd packages/flutter && ../../bin/flutter test --platform=chrome test/foundation/stack_frame_test.dart
  • ./bin/flutter test packages/flutter/test/foundation/stack_trace_test.dart packages/flutter/test/foundation/assertions_test.dart
  • ./bin/flutter test packages/flutter_test/test/demangle_test.dart packages/flutter_test/test/bindings_async_gap_test.dart packages/flutter_test/test/test_async_utils_test.dart

@gabrimatic
gabrimatic requested a review from justinmc May 12, 2026 22:53
@Piinks
Piinks self-requested a review July 13, 2026 22:12

@justinmc justinmc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

I'm convinced that this is the best approach for Flutter to take for the sake of our developers. It feels a bit weird hard coding in the "asynchronous gap" string from package:stack_trace, but I guess we were doing that before this PR anyway. This change will likely save many app users from experiencing a crash.

For the record, the original PR that added this assert is #60478.

@Piinks Piinks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM me as well

`StackFrame.fromStackTraceLine` used to assert when it encountered the
literal `===== asynchronous gap ===========================` line that
`package:stack_trace` emits between chained Dart-VM stacks. The
diagnostic told callers to wire up `FlutterError.demangleStackTrace`,
but utilities like `debugPrintStack` ran the parser unconditionally on
caller-supplied stacks (e.g. the one carried by `ParallelWaitError` or
by stacks that pass through the test runner) and crashed there before
the demangle hook could help.

That marker is the `package:stack_trace` equivalent of the VM's
`<asynchronous suspension>` line, so the parser now folds it into
`StackFrame.asynchronousSuspension` rather than asserting. Stacks that
carry the marker round-trip cleanly through `debugPrintStack` and
`FlutterError.defaultStackFilter` without losing the boundary.

Fixes flutter#179018
@Piinks
Piinks force-pushed the stack-frame-parallel-wait-async-gap branch from 46e6d69 to 087c682 Compare July 20, 2026 23:01
@Piinks Piinks added CICD Run CI/CD autosubmit Merge PR when tree becomes green via auto submit App labels Jul 20, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Jul 21, 2026
Merged via the queue into flutter:master with commit fb4c80b Jul 21, 2026
26 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jul 21, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Jul 21, 2026
flutter/flutter@cab057d...1ac2e82

2026-07-21 [email protected] Add BaseWindowController.isDestroyed flag (flutter/flutter#189061)
2026-07-21 [email protected] Roll Dart SDK from 666e1e2133b7 to 3b2f5ad7718d (1 revision) (flutter/flutter#189745)
2026-07-21 [email protected] Roll Skia from 3c52d80c960d to 569534e9fa59 (5 revisions) (flutter/flutter#189766)
2026-07-21 [email protected] Roll Skia from 11426cf7aaa1 to 3c52d80c960d (2 revisions) (flutter/flutter#189758)
2026-07-21 [email protected] Roll pub packages (flutter/flutter#189760)
2026-07-21 [email protected] [iOS] Inject DisplayLinkManager into FlutterKeyboardInsetManager (flutter/flutter#189751)
2026-07-21 [email protected] Treat package:stack_trace async-gap marker as asynchronous suspension (flutter/flutter#185791)
2026-07-21 [email protected] [iOS] Inject DisplayLinkManager into VsyncWaiterIOS (flutter/flutter#189749)
2026-07-20 [email protected] Use String builder in `InputConnectionAdaptorTest.java` (flutter/flutter#189281)
2026-07-20 [email protected] Fix non-independant tests in draggable_test.dart (flutter/flutter#186898)
2026-07-20 [email protected] Stop using ReLinker to load libflutter.so on Android 17 (API 37+) (flutter/flutter#189146)
2026-07-20 [email protected] [et] Refactor gn post-processing, fix minor clang[++] regex match bug (flutter/flutter#189685)
2026-07-20 [email protected] Take plugin_test_darwin out of bringup (flutter/flutter#189594)
2026-07-20 [email protected] Roll pub packages (flutter/flutter#189596)
2026-07-20 [email protected] widgets layer re-export ScrollCacheExtent (flutter/flutter#189483)
2026-07-20 [email protected] Roll Skia from 47143b6fa402 to 11426cf7aaa1 (7 revisions) (flutter/flutter#189740)
2026-07-20 [email protected] Sync CHANGELOG.md from stable (flutter/flutter#189735)
2026-07-20 [email protected] [Impeller] In the AHBTextureSourceVK destructor, do not call Vulkan APIs if the VkDevice has been destroyed (flutter/flutter#189666)
2026-07-20 [email protected] Add AutofillHints.emailOTPCode for Android AUTOFILL_HINT_EMAIL_OTP (flutter/flutter#188123)
2026-07-20 [email protected] Add a github workflow to remove flutteractionsbot branches after the PRs are merged or closed. (flutter/flutter#189668)
2026-07-20 [email protected] Roll ANGLE to cc08479fbcc1 (flutter/flutter#189595)
2026-07-20 [email protected] Roll Fuchsia Linux SDK from NL8xtzr8cxr5E8r8E... to GswhlPRO-D1qSNclx... (flutter/flutter#189715)
2026-07-20 [email protected] Roll Skia from ba90f98535de to 47143b6fa402 (4 revisions) (flutter/flutter#189721)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Zubii12 pushed a commit to Zubii12/packages that referenced this pull request Jul 23, 2026
…r#12256)

flutter/flutter@cab057d...1ac2e82

2026-07-21 [email protected] Add BaseWindowController.isDestroyed flag (flutter/flutter#189061)
2026-07-21 [email protected] Roll Dart SDK from 666e1e2133b7 to 3b2f5ad7718d (1 revision) (flutter/flutter#189745)
2026-07-21 [email protected] Roll Skia from 3c52d80c960d to 569534e9fa59 (5 revisions) (flutter/flutter#189766)
2026-07-21 [email protected] Roll Skia from 11426cf7aaa1 to 3c52d80c960d (2 revisions) (flutter/flutter#189758)
2026-07-21 [email protected] Roll pub packages (flutter/flutter#189760)
2026-07-21 [email protected] [iOS] Inject DisplayLinkManager into FlutterKeyboardInsetManager (flutter/flutter#189751)
2026-07-21 [email protected] Treat package:stack_trace async-gap marker as asynchronous suspension (flutter/flutter#185791)
2026-07-21 [email protected] [iOS] Inject DisplayLinkManager into VsyncWaiterIOS (flutter/flutter#189749)
2026-07-20 [email protected] Use String builder in `InputConnectionAdaptorTest.java` (flutter/flutter#189281)
2026-07-20 [email protected] Fix non-independant tests in draggable_test.dart (flutter/flutter#186898)
2026-07-20 [email protected] Stop using ReLinker to load libflutter.so on Android 17 (API 37+) (flutter/flutter#189146)
2026-07-20 [email protected] [et] Refactor gn post-processing, fix minor clang[++] regex match bug (flutter/flutter#189685)
2026-07-20 [email protected] Take plugin_test_darwin out of bringup (flutter/flutter#189594)
2026-07-20 [email protected] Roll pub packages (flutter/flutter#189596)
2026-07-20 [email protected] widgets layer re-export ScrollCacheExtent (flutter/flutter#189483)
2026-07-20 [email protected] Roll Skia from 47143b6fa402 to 11426cf7aaa1 (7 revisions) (flutter/flutter#189740)
2026-07-20 [email protected] Sync CHANGELOG.md from stable (flutter/flutter#189735)
2026-07-20 [email protected] [Impeller] In the AHBTextureSourceVK destructor, do not call Vulkan APIs if the VkDevice has been destroyed (flutter/flutter#189666)
2026-07-20 [email protected] Add AutofillHints.emailOTPCode for Android AUTOFILL_HINT_EMAIL_OTP (flutter/flutter#188123)
2026-07-20 [email protected] Add a github workflow to remove flutteractionsbot branches after the PRs are merged or closed. (flutter/flutter#189668)
2026-07-20 [email protected] Roll ANGLE to cc08479fbcc1 (flutter/flutter#189595)
2026-07-20 [email protected] Roll Fuchsia Linux SDK from NL8xtzr8cxr5E8r8E... to GswhlPRO-D1qSNclx... (flutter/flutter#189715)
2026-07-20 [email protected] Roll Skia from ba90f98535de to 47143b6fa402 (4 revisions) (flutter/flutter#189721)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

debugPrintStack throws an assertion error when printing the stack trace of a ParallelWaitError during tests

4 participants