Mobile: DM messages silently ship with zero p-tags when membership query races a relay reconnect
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
Research direction
Start in mobile/lib/features/channels/send_message_provider.dart at SendMessage._fetchDmRecipientPubkeys() and review the existing fallback tests for empty or failed membership queries. Verify the bounded retry behavior during transient empty results, including the 400ms delay and three-attempt limit, while keeping the change scoped to the DM mention path.
Written by the indexing model from the issue text.
Description
Summary
A plain, top-level DM message can go out with zero p tags — not a thread reply, not a hand-typed-mention issue (#4309), a genuinely fresh top-level message in an active group DM. Confirmed live: checked the raw event on the relay directly, and the channel's own member list (via buzz channels members) was correct and complete at the same moment — the recipients existed, the app just didn't attach them.
Root cause
SendMessage._fetchDmRecipientPubkeys() (mobile/lib/features/channels/send_message_provider.dart) resolves DM recipients from two sources, with a documented fallback:
List<ChannelMember>? members;
try {
members = await _fetchMembers(channelId);
} catch (_) {}
final participants = members != null && members.isNotEmpty
? members.map((member) => member.pubkey)
: channel.participantPubkeys;
This correctly falls back to channel.participantPubkeys (the channel metadata's p tags) when the live membership query throws or returns empty — and that fallback path has existing test coverage (falls back to metadata DM recipients when membership is empty / ...when membership fails).
The gap: both sources can be empty at the same moment, and nothing distinguishes that from "this DM genuinely has no other participants" — which is impossible by construction (a DM always has ≥1 other participant). channelMembersProvider (channel_management_provider.dart:483) returns a plain empty list, not an error, whenever the relay session isn't in SessionStatus.connected and there's no cached snapshot:
if (sessionState.status != SessionStatus.connected) {
final cachedMembers = snapshotCache.read(...);
if (cachedMembers != null) return cachedMembers;
final channelListMembers = ref.read(channelsProvider.notifier).cachedMembersForChannel(channelId);
if (channelListMembers.isNotEmpty) { ...; return channelListMembers; }
return const []; // <-- silent empty, not an error
}
Mobile reconnects the relay session far more often than desktop (iOS backgrounding suspends the socket — the same underlying pattern already confirmed in #6200's presence gap). If a send happens to land during one of those reconnect windows, and the in-memory Channel object's participantPubkeys also isn't populated at that moment (e.g. it hasn't been refreshed since this specific DM's metadata event was last parsed), both fallback layers come up empty and the message ships with no recipients — silently, with no error surfaced anywhere.
Fix
mobile/lib/features/channels/send_message_provider.dart: when both the live query and the metadata fallback come back empty, retry the membership fetch up to 3 times with a 400ms delay between attempts before giving up, since "zero participants" is never a valid answer for a real DM. Bounded, so a genuinely persistent failure still resolves (with zero recipients, same as before) rather than hanging the send indefinitely.
Scope note
channelMembersProvider's silent-empty-on-disconnect behavior is shared by 7 other consumers (members_sheet.dart, channel_actions_sheet.dart, mention_candidates_provider.dart, compose_bar/helpers.dart, compose_bar_widget.dart, working_bots_provider.dart, channel_detail_page.dart) — changing the provider's own error contract would touch all of them and needs real review, not a blind change. The fix here is scoped narrowly to the DM-mention path only.
Related
- #6199 / PR #6203 — a different, already-fixed bug in the same family:
ThreadDetailPagecould pass anullChannelobject into this same send path. That fix doesn't cover this case — this one reproduces on ordinary top-level DM messages wherechannelis never null, the contents of the membership data are just transiently empty. - #6200 — mobile's presence display has no backstop against the same frequent-reconnect pattern that causes this.
- Dominant language
- Rust
- Stars
- 33.7k
- Forks
- 4.4k
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 239
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from block/buzz
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
workflow_sink's mention parser never masks code regions — @name inside a code span wakes the agent Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 Half a day Newbie friendliness 88/100
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
gitbutlerapp/gitbutler#15998 · 1 comment ·
-
bug triage:deciding
Difficulty 1/5 Under an hour Newbie friendliness 88/100
open-telemetry/otel-arrow#4132 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100