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
Access & Control tab: permission-denied subject thumbnails cause repeated error toasts, flicker, and overlapping layout #434
On a deployed portal, opening the Access & Control settings tab of a node that carries node-scoped access assignments produces:
Wild flickering of the whole tab (rows repeatedly tearing down and re-rendering).
Repeated error toasts, one per affected assignment row, of the form:
Something went wrong
Loading thumbnail for <subject>: User <viewer> lacks Read permission on <subject>
Partly overlapping GUI graphics — assignment cards/controls render on top of each other while the tab is flickering.
Reproduction
On a node N inside a space, create node-scoped access: several AccessAssignment nodes under N/_Access/ plus a _Policy (PartitionAccessPolicy) with BreaksInheritance: true.
The assignment subjects are users whose own user partitions the viewer has no access to (the normal case — two users can share access to N without being able to read each other's partitions).
As one of the assignees, open N → settings → Access & Control tab.
AccessControlLayoutArea renders each assignment row via AccessAssignmentControlBuilder, which uses MeshNodeThumbnailControl pointed at the subject's node path (src/MeshWeaver.Graph/AccessAssignmentControlBuilder.cs).
MeshNodeThumbnailView.BindData() subscribes Hub.GetMeshNodeStream(NodePath) for that subject and forwards any stream error to SurfaceError(ex, "Loading thumbnail for {NodePath}") (src/MeshWeaver.Blazor/Components/MeshNodeThumbnailView.razor.cs:53-64) — i.e. a permission-denied read is surfaced as an error toast.
Reading another user's node is denied by design here: type-level public-read (node_type_permissions) still requires partition_access, so a subject living in a partition the viewer is not a member of can never be streamed. The thumbnail must treat this as a normal, expected state.
The area re-composes from nodeStream.CombineLatest(isAdminStream, ...) (src/MeshWeaver.Graph/AccessControlLayoutArea.cs:65), so every upstream emission rebuilds the rows, which re-subscribes every subject stream, which re-fails, which re-toasts → the flicker/toast loop. This matches the documented storm pattern (uncaught/re-fired errors on every emission) from ErrorPropagationAndWedges.
Expected behavior
A subject node the viewer cannot read renders a fallback card (initials avatar + subject id, which the assignment row already knows from the AccessAssignment content) — no error toast, at most one debug-level log line.
The Access & Control tab renders stably: no teardown/re-subscribe of unchanged rows on unrelated emissions, no flicker, no overlapping cards.
Notes for the fix (root cause, not band-aid)
Distinguish permission-denied from infrastructure errors in MeshNodeThumbnailView: denied → fallback render state; genuine faults → surface once, do not re-fire per emission.
Review the access-tab composition so rows are keyed/stable across CombineLatest emissions instead of being rebuilt (the rebuild is what turns one denied read into a repeating toast + flicker).
The overlapping graphics reproduce while the tab is in this rebuild loop and should be re-checked once the loop is fixed; if they persist, they are a separate layout defect of the assignment rows.
RCA (verified against source) — confirmed real in-repo bug
Each Access & Control assignment row renders a MeshNodeThumbnailControl bound to the subject's node path. MeshNodeThumbnailView.BindData (MeshNodeThumbnailView.razor.cs:53-64) subscribes Hub.GetMeshNodeStream(subjectPath) and forwards any stream error to SurfaceError. Reading a subject in a partition the viewer isn't a member of is denied by design: MeshNodeStreamCache.GateOnRead (MeshNodeStreamCache.cs:1000-1007) throws UnauthorizedAccessException("… lacks Read permission …") — the exact toast text. SurfaceError (BlazorView.razor.cs:421-447) then does three churny things per error: LogError (Loki cost), PortalErrorSink.Report → the "Something went wrong" toast, and RequestStateChange(). The flicker/overlap comes from AccessControlLayoutArea.AccessControl recomposing the whole control tree on every CombineLatest emission (:65-68), which re-instantiates each thumbnail → new denied subscription → new toast + log + StateHasChanged.
Fix (root cause, PR incoming): (1) classify permission-denied as a benign fallback, not an error — in MeshNodeThumbnailView's onError use the existing AreaErrorClassifier.IsExpectedUserActionFailure(ex) (AreaErrorClassifier.cs:100-119, already true for UnauthorizedAccessException): keep the seeded Title/roles, render the initials avatar, log Debug, and do NOT SurfaceError; only genuine infra faults surface, once. (2) stabilize the tab composition (DistinctUntilChanged/keyed rows) so unchanged rows aren't torn down + re-subscribed on every upstream tick. In-repo unit-testable (MonolithMeshTestBase + AddSampleUsers + RLS).
Activity
rbuergi commentedon Jul 18, 2026
RCA (verified against source) — confirmed real in-repo bug
Each Access & Control assignment row renders a
MeshNodeThumbnailControlbound to the subject's node path.MeshNodeThumbnailView.BindData(MeshNodeThumbnailView.razor.cs:53-64) subscribesHub.GetMeshNodeStream(subjectPath)and forwards any stream error toSurfaceError. Reading a subject in a partition the viewer isn't a member of is denied by design:MeshNodeStreamCache.GateOnRead(MeshNodeStreamCache.cs:1000-1007) throwsUnauthorizedAccessException("… lacks Read permission …")— the exact toast text.SurfaceError(BlazorView.razor.cs:421-447) then does three churny things per error:LogError(Loki cost),PortalErrorSink.Report→ the "Something went wrong" toast, andRequestStateChange(). The flicker/overlap comes fromAccessControlLayoutArea.AccessControlrecomposing the whole control tree on everyCombineLatestemission (:65-68), which re-instantiates each thumbnail → new denied subscription → new toast + log + StateHasChanged.Fix (root cause, PR incoming): (1) classify permission-denied as a benign fallback, not an error — in
MeshNodeThumbnailView's onError use the existingAreaErrorClassifier.IsExpectedUserActionFailure(ex)(AreaErrorClassifier.cs:100-119, already true forUnauthorizedAccessException): keep the seeded Title/roles, render the initials avatar, log Debug, and do NOTSurfaceError; only genuine infra faults surface, once. (2) stabilize the tab composition (DistinctUntilChanged/keyed rows) so unchanged rows aren't torn down + re-subscribed on every upstream tick. In-repo unit-testable (MonolithMeshTestBase + AddSampleUsers + RLS).Merge pull request #520 from Systemorph/fix/434-access-thumbnail-deni…
ci: gen-manifests.py becomes the platform's, configured per repo (Plu…