Audit finding from #474 (commit 958e1ec)
Severity: medium
Category: observability / ux
File: 11 sites across crates/web/src/components/
Obvious fix: yes
Description
#350 (GEN-07, closed via PR #443) fixed silent error-swallowing in crates/web/src/handlers.rs by introducing warn_and_toast / warn_and_toast_with helpers. Pattern landed cleanly there but was not propagated to component-internal handlers.
11 sites in crates/web/src/components/ still discard anyhow::Result<()> from client mutations w/ let _ = h.<method>().await:
crates/web/src/components/roles.rs:56 let _ = h.create_role(&name).await;
crates/web/src/components/roles.rs:207 let _ = h.set_permission(&rid, parsed, granted).await;
crates/web/src/components/roles.rs:246 let _ = h.assign_role(eid, &r).await;
crates/web/src/components/roles.rs:291 let _ = h.delete_role(&rid).await;
crates/web/src/components/settings.rs:58 let _ = h.set_server_display_name(&name).await;
crates/web/src/components/settings.rs:439 let _ = h.mutate_grove_mute(target).await;
crates/web/src/components/sync_queue_view.rs:80 let _ = h.retry_queue().await;
crates/web/src/components/channel_sidebar.rs:191 let _ = h.create_voice_channel(&name_owned).await;
crates/web/src/components/channel_sidebar.rs:194 let _ = h.create_channel(&name_owned).await;
crates/web/src/components/channel_sidebar.rs:662 let _ = h.delete_channel(&name).await;
crates/web/src/components/channel_sidebar.rs:978 let _ = h.mutate_channel_mute(&channel, target).await;
All are direct user actions: create/delete role, grant/revoke permission, assign role, set display name, mute grove/channel, retry sync queue, create/delete channel. Action fails → user sees nothing. Same UX bug class as #350.
Note: at least one of these (roles.rs:207 set_permission swallow) was added/touched in the typed-Permission rework window; the warn_and_toast pattern was never propagated outside handlers.rs.
Impact / Threat
User-facing — silent failures across server-management surfaces (roles, channels, settings, queue retry). Users can't tell whether action took effect, especially under flaky network. Identical UX to bug #350 fixed; this is the un-migrated tail.
Developer-facing — no tracing::warn! either, so failures invisible in field logs.
Suggested fix
Mechanical migration. Each site can use the existing helpers from handlers.rs:
// before
let _ = h.create_role(&name).await;
// after
if let Err(e) = h.create_role(&name).await {
crate::handlers::warn_and_toast_with("create role", &e, toasts.as_ref());
}
Pattern from handlers.rs (post-#350): capture let toasts = use_context::<ToastStack>(); on outer reactive frame before spawn_local, pass into warn_and_toast_with inside async body. wasm_bindgen_futures::spawn_local strips reactive owner, so naked use_context inside async block returns None.
Verify
rg -n "let _ = h\.[a-z_]+\(.*\)\.await" crates/web/src/components/ | wc -l
# 11
# After fix, expect: 0
Cross-ref
Audit finding from #474 (commit 958e1ec)
Severity: medium
Category: observability / ux
File: 11 sites across
crates/web/src/components/Obvious fix: yes
Description
#350 (GEN-07, closed via PR #443) fixed silent error-swallowing in
crates/web/src/handlers.rsby introducingwarn_and_toast/warn_and_toast_withhelpers. Pattern landed cleanly there but was not propagated to component-internal handlers.11 sites in
crates/web/src/components/still discardanyhow::Result<()>from client mutations w/let _ = h.<method>().await:All are direct user actions: create/delete role, grant/revoke permission, assign role, set display name, mute grove/channel, retry sync queue, create/delete channel. Action fails → user sees nothing. Same UX bug class as #350.
Note: at least one of these (roles.rs:207
set_permissionswallow) was added/touched in the typed-Permissionrework window; the warn_and_toast pattern was never propagated outsidehandlers.rs.Impact / Threat
User-facing — silent failures across server-management surfaces (roles, channels, settings, queue retry). Users can't tell whether action took effect, especially under flaky network. Identical UX to bug #350 fixed; this is the un-migrated tail.
Developer-facing — no
tracing::warn!either, so failures invisible in field logs.Suggested fix
Mechanical migration. Each site can use the existing helpers from
handlers.rs:Pattern from
handlers.rs(post-#350): capturelet toasts = use_context::<ToastStack>();on outer reactive frame beforespawn_local, pass intowarn_and_toast_withinside async body.wasm_bindgen_futures::spawn_localstrips reactive owner, so nakeduse_contextinside async block returnsNone.Verify
Cross-ref
handlers.rsonly. Pattern + helpers landed there; this finding = un-migrated component tail.