Skip to content

SemanticsNode._replaceChildren may cause inconsistent parent/children pointers, and eventually asserts #189902

Closed

Description

@xiangh-2018

Steps to reproduce

  1. run sample code;
  2. hover mouse pointer on "bad text" until "bad tooltip" shows;
  3. move mouse pointer out of "bad text" so that "bad tooltip" disappears;
  4. hover mouse pointer on "bad text" again until "bad tooltip" shows;
  5. move mouse pointer out of "bad text";

Expected results

at step 5, "bad tooltip" disappears;

Actual results

at step 5, asserts with:

_AssertionError ('package:flutter/src/semantics/semantics.dart': Failed assertion: line 3013 pos 16: '!child.attached': is not true.)

with stack like:

_AssertionError._doThrowNew (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/_internal/vm/lib/errors_patch.dart:67)
_AssertionError._throwNew (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/_internal/vm/lib/errors_patch.dart:49)
SemanticsNode._replaceChildren (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/semantics/semantics.dart:3013)
SemanticsNode.updateWith (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/semantics/semantics.dart:3779)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6258)
_RenderObjectSemantics._produceSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6272)
_RenderObjectSemantics._buildSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6205)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6241)
_RenderObjectSemantics._produceSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6272)
_RenderObjectSemantics._buildSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6205)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6241)
_RenderObjectSemantics._produceSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6272)
_RenderObjectSemantics._buildSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6205)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6241)
_RenderObjectSemantics._produceSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6272)
_RenderObjectSemantics._buildSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6205)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6241)
_RenderObjectSemantics.ensureSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6177)
PipelineOwner.flushSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:1636)
PipelineOwner.flushSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:1653)
RendererBinding.drawFrame (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/binding.dart:650)
WidgetsBinding.drawFrame (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/widgets/binding.dart:1573)
RendererBinding._handlePersistentFrameCallback (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/binding.dart:509)
SchedulerBinding._invokeFrameCallback (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/scheduler/binding.dart:1430)
SchedulerBinding.handleDrawFrame (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/scheduler/binding.dart:1345)
SchedulerBinding._handleDrawFrame (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/scheduler/binding.dart:1198)
_invoke (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/ui/hooks.dart:423)
PlatformDispatcher._drawFrame (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/ui/platform_dispatcher.dart:447)
_drawFrame (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/ui/hooks.dart:395)

(btw, the line-numbers do not match authentic SDK due to some debug code I've added, see attached. I'm sure those debug code do not cause the problem, ;)

debug.semantics.patch

I think a direct cause of the assert is this:

diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart
index 27a82695a98..d46fd5250d1 100644
--- a/packages/flutter/lib/src/semantics/semantics.dart
+++ b/packages/flutter/lib/src/semantics/semantics.dart
@@ -3003,6 +3007,7 @@ class SemanticsNode with DiagnosticableTreeMixin {
           // ancestors. In that case, we drop the child eagerly here.
           // TODO(ianh): Find a way to assert that the same node didn't
           // actually appear in the tree in two places.
+          // FIXME: child.parent?._children is **NOT UPDATED** !!!
           child.parent?._dropChild(child);
         }
         assert(!child.attached);

Code sample

Code sample
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() => runApp(
      const MaterialApp(
        home: Scaffold(
          body: TestWidget(),
        ),
      ),
    );

class TestWidget extends StatelessWidget {
  const TestWidget({super.key});

  Widget decorate(Widget child) {
    //return child;
    return DecoratedBox(
      decoration: BoxDecoration(border: Border.all()),
      child: child,
    );
  }

  @override
  Widget build(BuildContext context) {
    Widget goodTooltip = Tooltip(
      message: 'good tooltip',
      child: Semantics(
        explicitChildNodes: true,
        child: const Text('good text'),
      ),
    );

    // harmless tooltip;
    Widget badTooltip = Tooltip(
      message: 'bad tooltip',
      child: Semantics(
        explicitChildNodes: true,
        child: const Text('bad text'),
      ),
    );
    // must, to enable SemanticsNode "stealing";
    badTooltip = Overlay.wrap(child: ExcludeSemantics(child: badTooltip));
    // must too, to disable certain SemanticsNode rebuilding;
    badTooltip = SizedBox(width: 200, height: 100, child: badTooltip);

    Widget widget = Column(
      spacing: 20,
      children: [
        ExcludeSemantics(
          child: SizedBox(
            width: 200,
            height: 60,
            child: Center(
              child: TextButton(
                onPressed: () {
                  debugDumpSemanticsTree();
                },
                child: const Text('debugDumpSemanticsTree'),
              ),
            ),
          ),
        ),
        decorate(goodTooltip),
        decorate(badTooltip),
      ],
    );
    // this is also a must;
    widget = Dialog(child: widget);
    return widget;
  }
}

Logs

comments on log:

  • SemanticsNode, n#4: 29f17e56, n#5: fdb5516, n#6: 3747acb0;
  • regex: '21e214f0|f49ed51|c881c69'
  • ln#71, debugDumpSemanticsTree, n#4 owns n#5;
  • ln#98-129, show 'bad tooltip', n#4 owns n#6 owns n#5;
    • right after n#6 "steals" n#5 from n#4, n#4 temporarily contains an invalid
      ref to n#5 in its _children;
    • n#4._children is shortly overwritten in _replaceChildren;
  • ln#194-213, hide 'bad tooltip', n#4 owns n#5;
    • n#4 "steals" n#5 back from n#6;
    • n#6 is kept in related _RenderObjectSemantics.cacheSemanticsNode, and
      contains an invalid ref to n#5 in its _children;
  • ln#230-247, show 'bad tooltip' again;
    • should be n#4 owns n#6 owns n#5;
    • but for some reason (i can't explain), n#6 is not rebuilt;
    • if n#6 is rebuilt, n#5 would be "stolen" from n#4 before n#4's _replaceChildren;
    • without rebuild, n#5 is dropped in n#4's _replaceChildren (parent == null),
      but still ref'ed in n#6's _children, and attached (recursively in n#6's attach);
      • this can be caught by the commented asserting 'XXX: catch bad parent' in
        debug patch;
  • ln#312, hide 'bad tooltip' again; assert because n#5 is attached but has no parent;
  • ln#317, asserts;
Logs, with debug.semantics.patch applied
Launching /home/xiangh/flutter-arena/mysample/lib/main.dart on Linux in debug mode...
✓ Built build/linux/x64/debug/bundle/mysample
Gdk-Message: 09:27:42.972: Unable to load  from the cursor theme
Connecting to VM Service at ws://127.0.0.1:40245/ijPFzu3mCK8=/ws
Connected to the VM Service.
2026-07-23 09:27:44.651757: attach, cf98f, SemanticsNode#0(Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), invisible)

2026-07-23 09:27:44.678217: -------- _replaceChildren: fdb5516 --------
======== sawChange = false ========

2026-07-23 09:27:44.679673: -------- _replaceChildren: 29f17e56 --------
2026-07-23 09:27:44.680135: _adoptChild, 29f17e56 => fdb5516, SemanticsNode#4(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog) => SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    new: fdb5516, SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
======== sawChange = true ========

2026-07-23 09:27:44.687172: -------- _replaceChildren: 10889296 --------
2026-07-23 09:27:44.687275: _adoptChild, 10889296 => 29f17e56, SemanticsNode#3(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), flags: [scopesRoute]) => SemanticsNode#4(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog)
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    new: 29f17e56, SemanticsNode#4(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog)
======== sawChange = true ========

2026-07-23 09:27:44.697656: -------- _replaceChildren: 190eb487 --------
2026-07-23 09:27:44.697756: _adoptChild, 190eb487 => 10889296, SemanticsNode#2(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), sortKey: OrdinalSortKey#ee3e8(order: 0.0)) => SemanticsNode#3(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), flags: [scopesRoute])
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    new: 10889296, SemanticsNode#3(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), flags: [scopesRoute])
======== sawChange = true ========

2026-07-23 09:27:44.700380: -------- _replaceChildren: 211e9bc0 --------
2026-07-23 09:27:44.700480: _adoptChild, 211e9bc0 => 190eb487, SemanticsNode#1(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), textDirection: ltr) => SemanticsNode#2(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), sortKey: OrdinalSortKey#ee3e8(order: 0.0))
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    new: 190eb487, SemanticsNode#2(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), sortKey: OrdinalSortKey#ee3e8(order: 0.0))
======== sawChange = true ========

2026-07-23 09:27:44.702308: -------- _replaceChildren: cf98f --------
2026-07-23 09:27:44.702387: _adoptChild, cf98f => 211e9bc0, SemanticsNode#0(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0)) => SemanticsNode#1(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), textDirection: ltr)
2026-07-23 09:27:44.703029: attach, 211e9bc0, SemanticsNode#1(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), textDirection: ltr)
2026-07-23 09:27:44.703475: attach, 190eb487, SemanticsNode#2(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), sortKey: OrdinalSortKey#ee3e8(order: 0.0))
2026-07-23 09:27:44.703957: attach, 10889296, SemanticsNode#3(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), flags: [scopesRoute])
2026-07-23 09:27:44.704403: attach, 29f17e56, SemanticsNode#4(STALE, owner: null, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog)
2026-07-23 09:27:44.704857: attach, fdb5516, SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    new: 211e9bc0, SemanticsNode#1(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), textDirection: ltr)
======== sawChange = true ========

SemanticsNode#0
 │ Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0)
 │
 └─SemanticsNode#1
   │ Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0)
   │ textDirection: ltr
   │
   └─SemanticsNode#2
     │ Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0)
     │ sortKey: OrdinalSortKey#ee3e8(order: 0.0)
     │
     └─SemanticsNode#3
       │ Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0)
       │ flags: scopesRoute
       │
       └─SemanticsNode#4
         │ Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0)
         │ traversalParentIdentifier: _OverlayPortalState#18de2
         │ tooltip: "good tooltip"
         │ textDirection: ltr
         │ role: dialog
         │
         └─SemanticsNode#5
             Rect.fromLTRB(609.4, 104.0, 670.6, 124.0)
             label: "good text"
             textDirection: ltr

2026-07-23 09:27:50.638582: -------- _replaceChildren: 3747acb0 --------
2026-07-23 09:27:50.638816: _dropChild, 29f17e56 => fdb5516, SemanticsNode#4(Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog) => SemanticsNode#5(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
2026-07-23 09:27:50.639950: dettach, fdb5516, SemanticsNode#5(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
2026-07-23 09:27:50.640583: _adoptChild, 3747acb0 => fdb5516, SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr) => SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    new: fdb5516, SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
======== sawChange = true ========

2026-07-23 09:27:50.642597: -------- _replaceChildren: 37dd4e97 --------
======== sawChange = false ========

2026-07-23 09:27:50.642837: -------- _replaceChildren: 29f17e56 --------
2026-07-23 09:27:50.643029: _adoptChild, 29f17e56 => 3747acb0, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), role: dialog) => SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
2026-07-23 09:27:50.644005: attach, 3747acb0, SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
2026-07-23 09:27:50.644531: attach, fdb5516, SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
2026-07-23 09:27:50.644896: _adoptChild, 29f17e56 => 37dd4e97, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), role: dialog) => SemanticsNode#7(STALE, owner: null, Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
2026-07-23 09:27:50.645560: attach, 37dd4e97, SemanticsNode#7(STALE, owner: null, Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    old: fdb5516, SemanticsNode#5(dirty, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
    new: 3747acb0, SemanticsNode#6(dirty, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
    new: 37dd4e97, SemanticsNode#7(dirty, Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
======== sawChange = true ========

2026-07-23 09:27:50.649374: -------- _replaceChildren: 10889296 --------
======== sawChange = false ========

2026-07-23 09:27:50.649696: -------- _replaceChildren: 190eb487 --------
======== sawChange = false ========

2026-07-23 09:27:50.649930: -------- _replaceChildren: 211e9bc0 --------
======== sawChange = false ========

2026-07-23 09:27:50.650115: -------- _replaceChildren: cf98f --------
======== sawChange = false ========

2026-07-23 09:27:50.673959: -------- _replaceChildren: 30a3a84 --------
======== sawChange = false ========

2026-07-23 09:27:50.674329: -------- _replaceChildren: 37dd4e97 --------
2026-07-23 09:27:50.674465: _adoptChild, 37dd4e97 => 30a3a84, SemanticsNode#7(Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590) => SemanticsNode#8(STALE, owner: null, Rect.fromLTRB(69.7, 78.0, 130.3, 95.0), label: "bad tooltip", textDirection: ltr)
2026-07-23 09:27:50.675359: attach, 30a3a84, SemanticsNode#8(STALE, owner: null, Rect.fromLTRB(69.7, 78.0, 130.3, 95.0), label: "bad tooltip", textDirection: ltr)
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    new: 30a3a84, SemanticsNode#8(dirty, Rect.fromLTRB(69.7, 78.0, 130.3, 95.0), label: "bad tooltip", textDirection: ltr)
======== sawChange = true ========

2026-07-23 09:27:50.676561: -------- _replaceChildren: 29f17e56 --------
======== sawChange = false ========

2026-07-23 09:27:50.676729: -------- _replaceChildren: 10889296 --------
======== sawChange = false ========

2026-07-23 09:27:50.676875: -------- _replaceChildren: 190eb487 --------
======== sawChange = false ========

2026-07-23 09:27:50.677010: -------- _replaceChildren: 211e9bc0 --------
======== sawChange = false ========

2026-07-23 09:27:50.677141: -------- _replaceChildren: cf98f --------
======== sawChange = false ========

2026-07-23 09:27:51.473208: -------- _replaceChildren: 30a3a84 --------
======== sawChange = false ========

2026-07-23 09:27:51.473603: -------- _replaceChildren: 37dd4e97 --------
======== sawChange = false ========

2026-07-23 09:27:51.473913: -------- _replaceChildren: 29f17e56 --------
======== sawChange = false ========

2026-07-23 09:27:51.474216: -------- _replaceChildren: 10889296 --------
======== sawChange = false ========

2026-07-23 09:27:51.474539: -------- _replaceChildren: 190eb487 --------
======== sawChange = false ========

2026-07-23 09:27:51.474937: -------- _replaceChildren: 211e9bc0 --------
======== sawChange = false ========

2026-07-23 09:27:51.475297: -------- _replaceChildren: cf98f --------
======== sawChange = false ========

2026-07-23 09:27:51.501495: -------- _replaceChildren: 29f17e56 --------
2026-07-23 09:27:51.501691: _dropChild, 29f17e56 => 3747acb0, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog) => SemanticsNode#6(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
2026-07-23 09:27:51.503938: dettach, 3747acb0, SemanticsNode#6(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
2026-07-23 09:27:51.505054: dettach, fdb5516, SemanticsNode#5(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
2026-07-23 09:27:51.505758: _dropChild, 29f17e56 => 37dd4e97, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog) => SemanticsNode#7(Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
2026-07-23 09:27:51.506952: dettach, 37dd4e97, SemanticsNode#7(Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
2026-07-23 09:27:51.507483: dettach, 30a3a84, SemanticsNode#8(Rect.fromLTRB(69.7, 78.0, 130.3, 95.0), label: "bad tooltip", textDirection: ltr)
2026-07-23 09:27:51.508077: _dropChild, 3747acb0 => fdb5516, SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr) => SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
2026-07-23 09:27:51.509252: _adoptChild, 29f17e56 => fdb5516, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog) => SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
2026-07-23 09:27:51.510485: attach, fdb5516, SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    old: 3747acb0, SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
    old: 37dd4e97, SemanticsNode#7(STALE, owner: null, Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
    new: fdb5516, SemanticsNode#5(dirty, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
======== sawChange = true ========

2026-07-23 09:27:51.512809: -------- _replaceChildren: 10889296 --------
======== sawChange = false ========

2026-07-23 09:27:51.513011: -------- _replaceChildren: 190eb487 --------
======== sawChange = false ========

2026-07-23 09:27:51.513186: -------- _replaceChildren: 211e9bc0 --------
======== sawChange = false ========

2026-07-23 09:27:51.513345: -------- _replaceChildren: cf98f --------
======== sawChange = false ========

2026-07-23 09:27:52.353982: -------- _replaceChildren: 35ab8895 --------
======== sawChange = false ========

2026-07-23 09:27:52.354179: -------- _replaceChildren: 29f17e56 --------
2026-07-23 09:27:52.354246: _dropChild, 29f17e56 => fdb5516, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), role: dialog) => SemanticsNode#5(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
2026-07-23 09:27:52.355126: dettach, fdb5516, SemanticsNode#5(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
2026-07-23 09:27:52.355539: _adoptChild, 29f17e56 => 3747acb0, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), role: dialog) => SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
2026-07-23 09:27:52.356303: attach, 3747acb0, SemanticsNode#6(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
2026-07-23 09:27:52.356690: attach, fdb5516, SemanticsNode#5(STALE, owner: null, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
2026-07-23 09:27:52.356993: _adoptChild, 29f17e56 => 35ab8895, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), role: dialog) => SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
2026-07-23 09:27:52.357599: attach, 35ab8895, SemanticsNode#9(STALE, owner: null, Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    old: fdb5516, SemanticsNode#5(dirty, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr)
    new: 3747acb0, SemanticsNode#6(dirty, Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
    new: 35ab8895, SemanticsNode#9(dirty, Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
======== sawChange = true ========

2026-07-23 09:27:52.360040: -------- _replaceChildren: 10889296 --------
======== sawChange = false ========

2026-07-23 09:27:52.360288: -------- _replaceChildren: 190eb487 --------
======== sawChange = false ========

2026-07-23 09:27:52.360441: -------- _replaceChildren: 211e9bc0 --------
======== sawChange = false ========

2026-07-23 09:27:52.360578: -------- _replaceChildren: cf98f --------
======== sawChange = false ========

2026-07-23 09:27:52.369454: -------- _replaceChildren: 32fcf788 --------
======== sawChange = false ========

2026-07-23 09:27:52.369683: -------- _replaceChildren: 35ab8895 --------
2026-07-23 09:27:52.369778: _adoptChild, 35ab8895 => 32fcf788, SemanticsNode#9(Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590) => SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(69.7, 78.0, 130.3, 95.0), label: "bad tooltip", textDirection: ltr)
2026-07-23 09:27:52.370738: attach, 32fcf788, SemanticsNode#10(STALE, owner: null, Rect.fromLTRB(69.7, 78.0, 130.3, 95.0), label: "bad tooltip", textDirection: ltr)
stack: ...
#1      SemanticsNode._replaceChildren (package:flutter/src/semantics/semantics.dart:3097:6)
#2      SemanticsNode.updateWith (package:flutter/src/semantics/semantics.dart:3778:5)
#3      _RenderObjectSemantics._buildSemanticsSubtree (package:flutter/src/rendering/object.dart:6258:12)
#4      _RenderObjectSemantics._produceSemanticsNode (package:flutter/src/rendering/object.dart:6272:5)
...
    new: 32fcf788, SemanticsNode#10(dirty, Rect.fromLTRB(69.7, 78.0, 130.3, 95.0), label: "bad tooltip", textDirection: ltr)
======== sawChange = true ========

2026-07-23 09:27:52.372299: -------- _replaceChildren: 29f17e56 --------
======== sawChange = false ========

2026-07-23 09:27:52.372526: -------- _replaceChildren: 10889296 --------
======== sawChange = false ========

2026-07-23 09:27:52.373345: -------- _replaceChildren: 190eb487 --------
======== sawChange = false ========

2026-07-23 09:27:52.373795: -------- _replaceChildren: 211e9bc0 --------
======== sawChange = false ========

2026-07-23 09:27:52.374156: -------- _replaceChildren: cf98f --------
======== sawChange = false ========

2026-07-23 09:27:53.190395: -------- _replaceChildren: 32fcf788 --------
======== sawChange = false ========

2026-07-23 09:27:53.191011: -------- _replaceChildren: 35ab8895 --------
======== sawChange = false ========

2026-07-23 09:27:53.191467: -------- _replaceChildren: 29f17e56 --------
======== sawChange = false ========

2026-07-23 09:27:53.192047: -------- _replaceChildren: 10889296 --------
======== sawChange = false ========

2026-07-23 09:27:53.192603: -------- _replaceChildren: 190eb487 --------
======== sawChange = false ========

2026-07-23 09:27:53.193190: -------- _replaceChildren: 211e9bc0 --------
======== sawChange = false ========

2026-07-23 09:27:53.193805: -------- _replaceChildren: cf98f --------
======== sawChange = false ========

2026-07-23 09:27:53.221394: -------- _replaceChildren: 29f17e56 --------
2026-07-23 09:27:53.221584: _dropChild, 29f17e56 => 3747acb0, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog) => SemanticsNode#6(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
2026-07-23 09:27:53.223268: dettach, 3747acb0, SemanticsNode#6(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr)
2026-07-23 09:27:53.224115: _dropChild, 29f17e56 => 35ab8895, SemanticsNode#4(dirty, Rect.fromLTRB(0.0, 0.0, 1280.0, 720.0), traversalParentIdentifier: _OverlayPortalState#18de2, tooltip: "good tooltip", textDirection: ltr, role: dialog) => SemanticsNode#9(Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
2026-07-23 09:27:53.225472: dettach, 35ab8895, SemanticsNode#9(Rect.fromLTRB(540.0, 144.0, 740.0, 244.0), traversalChildIdentifier: _OverlayPortalState#7e590)
2026-07-23 09:27:53.226023: dettach, 32fcf788, SemanticsNode#10(Rect.fromLTRB(69.7, 78.0, 130.3, 95.0), label: "bad tooltip", textDirection: ltr)


==== asserts ! stack:
_AssertionError._doThrowNew (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/_internal/vm/lib/errors_patch.dart:67)
_AssertionError._throwNew (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/_internal/vm/lib/errors_patch.dart:49)
SemanticsNode._replaceChildren (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/semantics/semantics.dart:3013)
SemanticsNode.updateWith (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/semantics/semantics.dart:3779)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6258)
_RenderObjectSemantics._produceSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6272)
_RenderObjectSemantics._buildSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6205)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6241)
_RenderObjectSemantics._produceSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6272)
_RenderObjectSemantics._buildSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6205)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6241)
_RenderObjectSemantics._produceSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6272)
_RenderObjectSemantics._buildSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6205)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6241)
_RenderObjectSemantics._produceSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6272)
_RenderObjectSemantics._buildSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6205)
_RenderObjectSemantics._buildSemanticsSubtree (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6241)
_RenderObjectSemantics.ensureSemanticsNode (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:6177)
PipelineOwner.flushSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:1636)
PipelineOwner.flushSemantics (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/object.dart:1653)
RendererBinding.drawFrame (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/binding.dart:650)
WidgetsBinding.drawFrame (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/widgets/binding.dart:1573)
RendererBinding._handlePersistentFrameCallback (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/rendering/binding.dart:509)
SchedulerBinding._invokeFrameCallback (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/scheduler/binding.dart:1430)
SchedulerBinding.handleDrawFrame (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/scheduler/binding.dart:1345)
SchedulerBinding._handleDrawFrame (/DATA1/FLUTTER_SDK/flutter.latest/packages/flutter/lib/src/scheduler/binding.dart:1198)
_invoke (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/ui/hooks.dart:423)
PlatformDispatcher._drawFrame (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/ui/platform_dispatcher.dart:447)
_drawFrame (/DATA1/FLUTTER_SDK/flutter.latest/bin/cache/pkg/sky_engine/lib/ui/hooks.dart:395)

# select frame 2:
child
SemanticsNode (SemanticsNode#5(Rect.fromLTRB(609.4, 104.0, 670.6, 124.0), label: "good text", textDirection: ltr))
_actions: Map (0 items)
_actionsAsBits: 0
_areUserActionsBlocked: false
_attributedDecreasedValue: AttributedString (LateInitializationError: Field '_toStringVisiting@10040228' has been assigned during initialization.)
_attributedHint: AttributedString (AttributedString('', attributes: []))
_attributedIncreasedValue: AttributedString (LateInitializationError: Field '_toStringVisiting@10040228' has been assigned during initialization.)
_attributedLabel: AttributedString (AttributedString('good text', attributes: []))
_attributedValue: AttributedString (AttributedString('', attributes: []))
_children: List (0 items)
_controlsNodes: null
_currentValueLength: null
_customSemanticsActions: Map (0 items)
_dead: false
_debugPreviousSnapshot: List (0 items)
_depth: 6
_dirty: false
_flags: SemanticsFlags
_headingLevel: 0
_hintOverrides: null
_hitTestBehavior: SemanticsHitTestBehavior (SemanticsHitTestBehavior.defer)
_id: 5
_identifier: ""
_inputType: SemanticsInputType (SemanticsInputType.none)
_isMergedIntoParent: false
_isMultiline: false
_linkUrl: null
_locale: null
_maxValue: null
_maxValueLength: null
_mergeAllDescendantsIntoThisNode: false
_minValue: null
_owner: SemanticsOwner (SemanticsOwner#45594)
_parent: null
_platformViewId: null
_rect: Rect (Rect.fromLTRB(0.0, 0.0, 61.2, 20.0))
_role: SemanticsRole (SemanticsRole.none)
_scrollChildCount: null
_scrollExtentMax: null
_scrollExtentMin: null
_scrollIndex: null
_scrollPosition: null
_showOnScreen: Closure
_sortKey: null
_textDirection: TextDirection (TextDirection.ltr)
_textSelection: null
_tooltip: ""
_transform: Matrix4 ([0] [1.0,0.0,0.0,609.3841094970703]
[1] [0.0,1.0,0.0,104.0]
[2] [0.0,0.0,1.0,0.0]
[3] [0.0,0.0,0.0,1.0]
)
_traversalChildIdentifier: null
_traversalChildTransform: null
_traversalParent: null
_traversalParentIdentifier: null
_validationResult: SemanticsValidationResult (SemanticsValidationResult.none)
areUserActionsBlocked: false
attached: true
attributedDecreasedValue: AttributedString (AttributedString('', attributes: []))
attributedHint: AttributedString (AttributedString('', attributes: []))
attributedIncreasedValue: AttributedString (AttributedString('', attributes: []))
attributedLabel: AttributedString (AttributedString('good text', attributes: []))
attributedValue: AttributedString (AttributedString('', attributes: []))
childrenCount: 0
childrenCountInTraversalOrder: 0
controlsNodes: null
currentValueLength: null
debugIsDirty: false
decreasedValue: ""
depth: 6
flagsCollection: SemanticsFlags
hasChildren: false
hashCode: 266032406
headingLevel: 0
hint: ""
hintOverrides: null
hitTestBehavior: SemanticsHitTestBehavior (SemanticsHitTestBehavior.defer)
id: 5
identifier: ""
increasedValue: ""
indexInParent: null
inputType: SemanticsInputType (SemanticsInputType.none)
isInvisible: false
isMergedIntoParent: false
isMultiline: false
isPartOfNodeMerging: false
key: null
label: "good text"
linkUrl: null
maxValue: null
maxValueLength: null
mergeAllDescendantsIntoThisNode: false
minValue: null
owner: SemanticsOwner (SemanticsOwner#45594)
parent: null
parentPaintClipRect: null
parentSemanticsClipRect: null
platformViewId: null
rect: Rect (Rect.fromLTRB(0.0, 0.0, 61.2, 20.0))
role: SemanticsRole (SemanticsRole.none)
runtimeType: Type (SemanticsNode)
scrollChildCount: null
scrollExtentMax: null
scrollExtentMin: null
scrollIndex: null
scrollPosition: null
sortKey: null
tags: null
textDirection: TextDirection (TextDirection.ltr)
textSelection: null
tooltip: ""
transform: Matrix4 ([0] [1.0,0.0,0.0,609.3841094970703]
[1] [0.0,1.0,0.0,104.0]
[2] [0.0,0.0,1.0,0.0]
[3] [0.0,0.0,0.0,1.0]
)
traversalChildIdentifier: null
traversalParent: null
traversalParentIdentifier: null
validationResult: SemanticsValidationResult (SemanticsValidationResult.none)
value: ""

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.44.5, on Debian GNU/Linux 12 (bookworm) 6.1.0-49-amd64, locale en_US.UTF-8) [30ms]
    • Flutter version 3.44.5 on channel stable at /DATA1/FLUTTER_SDK/flutter.latest
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision f94f4fc76b (2 weeks ago), 2026-07-06 11:19:24 -0700
    • Engine revision 83675ed276
    • Dart version 3.12.2
    • DevTools version 2.57.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, enable-custom-devices, cli-animations, enable-native-assets,
      enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration

[✗] Android toolchain - develop for Android devices [23ms]
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/to/linux-android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, please use
      `flutter config --android-sdk` to update to that location.


[✓] Chrome - develop for the web [11ms]
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop [161ms]
    • Debian clang version 14.0.6
    • cmake version 3.25.1
    • ninja version 1.11.1
    • pkg-config version 1.8.1
    • GL_EXT_framebuffer_blit: no
    • GL_EXT_texture_format_BGRA8888: no

[✓] Connected device (2 available) [61ms]
    • Linux (desktop) • linux  • linux-x64      • Debian GNU/Linux 12 (bookworm) 6.1.0-49-amd64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 149.0.7827.200


[!] Network resources [131.1s]          
    ✗ A network error occurred while checking "https://maven.google.com/": Connection failed

! Doctor found issues in 2 categories.

Activity

  1. changed the title [-]SemanticsNode._replaceChildren may cause inconsistent parent/children pointers[/-] [+]SemanticsNode._replaceChildren may cause inconsistent parent/children pointers, and eventually asserts[/+] on Jul 23, 2026
  2. xiangh-2018 commented on Jul 23, 2026

    @xiangh-2018
    Author

    This (#187198 (comment)) looks similar.

  3. xiangh-2018 commented on Jul 23, 2026

    @xiangh-2018
    Author

    This might be a simple fix:

    diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart
    index 27a82695a98..405f8a9e8c8 100644
    --- a/packages/flutter/lib/src/semantics/semantics.dart
    +++ b/packages/flutter/lib/src/semantics/semantics.dart
    @@ -3003,7 +3003,13 @@ class SemanticsNode with DiagnosticableTreeMixin {
               // ancestors. In that case, we drop the child eagerly here.
               // TODO(ianh): Find a way to assert that the same node didn't
               // actually appear in the tree in two places.
    -          child.parent?._dropChild(child);
    +          // XXX: _dropChild & _adoptChild don't seem to touch _children,
    +          // leave them be;
    +          // here & right above are the only 2 refs to _dropChild; the above
    +          // one is taken care of by the final '_children = newChildren';
    +          final p = child.parent!;
    +          p._children?.remove(child);
    +          p._dropChild(child);
             }
             assert(!child.attached);
             _adoptChild(child);

    But I'm afraid this behavior is triggered by some recent addition: this code fragment has been around for years, and the assert only pops after a recent upgrade from 3.38.5 to 3.44.5;

  4. suryaKommana2662 commented on Jul 23, 2026

    @suryaKommana2662
    Contributor

    @xiangh-2018 I was able to reproduce the reported issue locally using the provided sample. Following the reported steps of hovering over bad text, moving the pointer away, and repeating the hover/unhover sequence, I encountered the following assertion:

    'package:flutter/src/semantics/semantics.dart':
    Failed assertion: '!child.attached': is not true.
    

    The stack trace points to SemanticsNode._replaceChildren during the semantics tree update/flush process, which matches the behavior described in this issue.

    I also noticed the related behavior reported in #187198. Although both issues trigger the same !child.attached assertion in SemanticsNode._replaceChildren, the reproduction scenarios are different. I'll keep this issue open for now so the team-accessibility can investigate whether both issues share the same underlying cause or require separate fixes.

  5. added
    a: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)
    team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)
    and removed
    in triagePresently being triaged by the triage team
    on Jul 23, 2026
  6. madenvel commented on Jul 27, 2026

    @madenvel

    Another assert variant, likely the same stale parent/children pointers. On Flutter 3.44.8 stable, the semantics flush asserts in _SemanticsGeometry.computeChildGeometry:

    ​ 'package:flutter/src/rendering/object.dart': Failed assertion: line 6670 pos 12: 'identical(childRenderObject, parentRenderObject)': is not true. #2 _SemanticsGeometry.computeChildGeometry (rendering/object.dart:6670) #3 _RenderObjectSemantics._updateChildGeometry (rendering/object.dart:6108) #4 _RenderObjectSemantics.ensureGeometry (rendering/object.dart:6098) #5 PipelineOwner.flushSemantics (rendering/object.dart:1590) ​

    i.e. the semantics tree holds a parent/child pair whose render objects are no longer ancestor/descendant. No tooltip interaction in my case — it fires intermittently around the app being backgrounded / losing focus, which dismisses hover state and overlays. Debug build, Linux desktop embedder (Fedora 44, GNOME with AT-SPI active). Happy to provide more detail if useful.

  7. added
    P2Important issues not at the top of the work list
    on Jul 29, 2026
  8. CanArslanDev commented on Aug 3, 2026

    @CanArslanDev
    Contributor

    @xiangh-2018 your analysis here pointed me at the right place, thanks. I went through the same code paths from the other direction (I have been chasing this in #187198, there under my other account @NL-CanArslan) and I think the missing link is that attach() and detach() disagree about stale child lists.

    detach() explicitly tolerates a stale _children, and comments on it:

    // semantics.dart:3284 (3.44.8 stable, 058e0af2c2)
    if (child.parent == this) {
      child.detach();
    }

    attach() has no such check (semantics.dart:3259), so a node holding a stale list re-attaches children that now belong to someone else. That produces a node with attached == true and parent == null, which is precisely the state your log shows for n#5 before the assertion fires.

    That also explains why n#6 is not rebuilt, which you said you could not account for. _RenderObjectSemantics._buildSemantics short circuits on built (object.dart:6187) and never reaches _buildSemanticsSubtree, so the cached node's _children is never refreshed. Nothing sets built = false when a child is stolen, because markNeedsBuild (object.dart:5748) only reacts to config, geometry and parentData, and there is no back reference from a SemanticsNode to the _RenderObjectSemantics holding it in cachedSemanticsNode.

    The three line alternative to your patch is to mirror the guard detach() already has:

         if (_children != null) {
           for (final SemanticsNode child in _children!) {
    -        child.attach(owner);
    +        // The list of children may be stale and may contain nodes that have
    +        // been assigned to a different parent.
    +        if (child.parent == this) {
    +          child.attach(owner);
    +        }
           }
         }

    I reduced your repro to a widget test, ran it against 3.44.8 stable and it fails with the same !child.attached at semantics.dart:3008. With the guard it passes, and the framework suites stay green (892 in test/semantics and test/rendering, 7014 in test/widgets with only pre-existing golden failures). Full writeup, both tests and the patch are in my comment on #187198: #187198 (comment)

    Two things about your proposed patch, for what they are worth. Mutating p._children works against the invariant the framework documents in detach(), and _children can be the const <SemanticsNode>[] that updateWith substitutes when the caller passes null (semantics.dart:3750), where remove would throw.

    On your suspicion that this came from a recent change, I think you are right and it is narrower than you might expect:

    $ git log -S"traversalParentIdentifier" -- packages/flutter/lib/src/semantics/semantics.dart
    fccfa978a97  2025-11-10  Reland "Refactor OverlayPortal semantics" (#178095)
    
    $ git tag --contains fccfa978a97 | grep -E '^3\.[0-9]+\.[0-9]+$' | head -1
    3.41.0
    

    The traversal grafting machinery landed in stable at 3.41.0, which is inside your 3.38.5 to 3.44.5 upgrade window. Also worth knowing: RawTooltip is built on OverlayPortal.overlayChildLayoutBuilder (raw_tooltip.dart:886), so your Tooltip repro is an OverlayPortal repro. Given that, and #182604 naming fccfa978a97 in its title, I would say this issue, #187198 and #182604 are all the same defect.

    @madenvel your identical(childRenderObject, parentRenderObject) variant at object.dart:6670 is the same corruption seen from the _RenderObjectSemantics side, but I do not think the guard above covers it. That one probably needs the cache invalidation itself rather than a defensive check at attach time.

  9. xiangh-2018 commented on Aug 4, 2026

    @xiangh-2018
    Author

    Hello @CanArslanDev, thanks for the detailed explanation. I agree deep down this is also an OverlayPortal issue.

  10. CanArslanDev commented on Sep 6, 2026

    @CanArslanDev
    Contributor

    Update on this one, since the fix moved after my earlier comment here.

    I proposed mirroring the detach() guard in attach() above. That is not what #190431 does any more. @chunhtai pointed out on the PR that the victim render object should have known its children were taken and marked itself, and after measuring it that turned out to be the actual gap:

    // object.dart, end of shouldFormSemanticsNode
    return parentData!.explicitChildNodes || _hasSiblingConflict;

    Both halves change whether a fragment forms its own semantics node. Only the first invalidates anything. _didUpdateParentData calls markNeedsBuild; markSiblingConfigurationConflict assigns the flag and returns. That is how a fragment ends up keeping built and its cachedSemanticsNode across a hide and show, so _buildSemantics short circuits, updateWith is never reached, and it goes back into the tree still holding _children that belong to somebody else. Exactly the "n#6 is not rebuilt" step you could not account for, @xiangh-2018.

    The PR now marks the fragment when that flag changes, in _marksConflictsInMergeGroup, which runs in the parent data phase so the rebuild happens in the same flush. The attach guard and the copy on write repair are both dropped. Your repro from the top of this issue, reduced to a widget test, fails on master with the same '!child.attached' and passes with it.

    @madenvel, your computeChildGeometry variant is still not accounted for. I said earlier that I expected it to survive any fix here, but that was about the guard, and this version rebuilds the fragment, which refreshes its geometry too, so I honestly do not know either way. I could not reproduce it on macOS. If it would be useful I would rather see it tracked in its own issue than lose it when this one is closed by the PR.

  11. github-actions commented on Oct 17, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  12. locked as resolved and limited conversation to collaborators on Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)triaged-accessibilityTriaged by Framework Accessibility team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions