Repository navigation
fix: semantics crash on the system-cell tooltips, and a group deleted mid-sync (#350, #356) - #357
Merged
Merged
Conversation
) The app died mid-session with an engine-level error and no Dart stack: accessibility_bridge.cc(114) Failed to update ui::AXTree, error: 1140 will not be in the tree and is not the new root This is upstream flutter/flutter#182444, diagnosed in flutter/flutter#190344 and flutter/flutter#190431, both still open and in neither 3.44.0 nor any later stable. A Tooltip is an OverlayPortal whose overlay half is joined to its anchor by a traversal-parent identifier; that identifier is silently dropped when the anchor's SemanticsConfiguration is absorbed into a neighbour's. The orphaned node is then serialized as a child nobody can reach, and the Windows embedder rejects the whole update rather than the one node. The accessibility tree freezes for the life of the window, and the process has been observed to die outright. SystemIndicatorCell writes the exact recipe: three adjacent tooltips in a row of a ListView, whose viewport describes itself with two-pane semantics. Asking for a semantics container leaves nothing to absorb the anchor's config, so the identifier survives. The new test replays the real SemanticsUpdateBuilder traffic through the engine's reachability rule, so it fails on Windows' terms without needing Windows: ~90 rejected updates for a five-row mouse sweep before this change, none after. Deliberately not changed: ReadOnlyLock, the app's other bare tooltip in a scrolling row. Measured against the same harness a lone tooltip anchor records no rejection — the fault needs a second overlay showing while the first is still dismissing — so it is left alone and swept by the test instead. IconButton(tooltip:) is immune for the same reason it already gives its anchor a node of its own. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
…356) `GroupManager.listGroups` fans out one member read per row the listing returned, so it is walking a list of a live directory that other writers keep changing. A group deleted inside that window answered its member read with `404 Request_ResourceNotFound`, which propagated out of `AzureConnector.sync` and killed the entire Azure pull: no snapshot at all, and an operator left with nothing but an object id to look up. That group is now dropped from the snapshot — which is what the directory now says about it — with a Dutch log line naming it (#266) and a closing summary that says how many were skipped rather than reporting a short pull as a complete one. The tolerance is deliberately narrow, and that is the whole risk of the change. `GraphException.isResourceNotFound` matches on Graph's own error code and not on the status, so a 401/403, a 429, a 5xx, a 404 with no Graph error envelope, and a 404 from the listing leg itself all still propagate and still fail the sync. A blanket catch here would report an expired token as a tenant with no groups, and downstream an absent Azure group reads as one to create or as a class that is gone — a quiet failure would come back as a screen full of destructive proposals. `loadByMailNicknames` (#280) has the same fan-out and gets the same treatment. Nothing else does: the other multi-item reads in `group_manager.dart`/`user_manager.dart` are `$filter` collection queries, where a vanished object simply is not in the result set, `$batch` membership writes that already report per-member outcomes, or `getUser`, which has always returned null on a 404. The member read also tells the transport this reply is expected (#229), so a pull that recovered does not leave a red line in the operator's log. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two unrelated robustness fixes, one commit each. Both came out of failures
observed during this batch rather than from code reading.
fix(ui)— give each system-cell tooltip its own semantics container (#350)The Windows app died with an engine-level error and took the session with it:
Root cause, established rather than guessed. It is upstream
flutter/flutter#182444
(open, P2), diagnosed in flutter/flutter#190344 and #190431 — both still open,
so no Flutter release contains the fix and upgrading would not have helped. A
Tooltipis anOverlayPortalwhose overlay is joined to its anchor by atraversal-parent identifier. That identifier is silently dropped when the
anchor's
SemanticsConfigurationis absorbed into a neighbour's; the overlay isthen serialized as an unreachable child, and the Windows embedder rejects the
entire update rather than the one node.
SystemIndicatorCellwrites that recipe exactly — three adjacentTooltips ina
ListViewrow — and it is on the Personeel details pane where the crash wasseen.
Semantics(container: true)gives each anchor a node of its own sonothing absorbs it.
Reproduced offline before being fixed. The regression test is a harness that
records real
SemanticsUpdateBuildertraffic and replays it through theengine's own reachability rule. It reproduces the failure deterministically
(~90 rejected updates per five-row mouse sweep) and discriminates cleanly:
IconButton(tooltip:)Semantics(container: true)(this fix)excludeFromSemanticsExcludeSemanticsThe last two match the upstream reporter's own measurements, which is the check
that says the harness is modelling the real defect and not something adjacent.
Deliberately left alone:
ReadOnlyLockand the settingsMenuAnchors /IconButtons. A lone anchor cannot reach this state and button-based anchorsalready get their own node, so changing them would have been unverifiable churn.
fix(azure)— tolerate a group deleted between listing and member read (#356)listGroupsfans out oneloadMemberIdscall per group. A group deleted in thetenant inside that window returned
404 Request_ResourceNotFoundand theexception propagated out of
AzureConnector.sync, failing the whole pull.In production that means an operator's sync dies because somebody deleted a Team
while it was running.
Both per-group member fan-outs (
listGroups,loadByMailNicknames) now dropsuch a group and continue, logging it in Dutch by
displayName+ object id witha "N overgeslagen" clause on the summary.
The tolerance is deliberately narrow, because the failure mode of getting
this wrong is worse than the bug: a blanket
catchwould report an expiredtoken as a tenant with no groups, and the action engine reads "no Azure group"
as "create it" / "it is gone" — turning an auth error into a pile of destructive
proposals. Matching is on the Graph error code via a new
GraphException.isResourceNotFound, not on the status. Still throwing: 401,403, 429, 5xx, a 404 with no Graph error envelope, a 404 carrying any other
code, a 404 from the listing leg itself, and a direct
loadMemberIdscall.The other multi-item reads were checked and are not of this shape:
loadByEmployeeIdsandloadByMailNicknames' chunk loop are$filtercollection queries where a vanished object is simply absent from the result set;
the
$batchmembership writes already surface per-member outcomes;getUseralready returns
nullon 404.Also plumbed
GraphClient.getCollection'sexpectedpredicate so a handled 404logs as a detail rather than an error (#229) — without it a fully recovered pull
still painted the operator's log red.
How #356 was found, and why it is not a regression from #355
The
developpost-merge sweep after #355 went red on the live Azure job. It wasnot caused by that PR, and the evidence is worth recording because the next
reader will assume it was:
2b5b1ef..3be60a7touched 22 files, none underpackages/azure_api/;and on PR feat: "Medewerker uit dienst" — a per-record staff departure command (#349) #353 before that;
az ad group show --group 523cfd6a-4ba5-4cb1-9231-5bf0d66d7656confirms theobject no longer exists in the tenant.
So a group was enumerated and then deleted inside the sync window — live
directory state moving under a read, not repo history. The test was right to go
red; the connector was wrong to die. #356 is that fix.
Test plan
Run on the branch tip (
894efc6):dart format --output=none --set-exit-if-changed .— cleandart analyze --fatal-infos --fatal-warnings— cleandart test packages/*/test— 1678 passed / 12 skipped(1658/12 at
develop; +20 from [Bug] A group deleted mid-sync fails the entire Azure pull #356)flutter test(account_manager) — 614 passed(612 at
develop; +2 from [Bug] App dies with an accessibility_bridge AXTree update failure ("Lost connection to device") #350)flutter test integration_test/app_launch_test.dart -d windows—147 passed, unchanged
Both fixes were verified to fail without them: #350 via the harness table above,
#356 by stashing
lib/and confirming 6 behavioural failures including theconnector sync aborting.
No live API calls were made by either worker — #356's verification is unit-level
against a fake transport, which is what its acceptance criteria asked for.
Caveats worth carrying forward
framework bug is still live. Any future pair of adjacent bare
Tooltips ina scrolling row reintroduces it. The new test is the tripwire.
timing-sensitive upstream and the integration-test driver never sweeps the
mouse across those cells — which is exactly why the Windows integration job
passed on feat: "Medewerker uit dienst" — a per-record staff departure command (#349) #353 and fix: scope the Smartschool pull, show department schools, fix shared-mail precedence (#351, #352, #354) #355 while the bug was live.
Closes #350
Closes #356