← Back to PRs

#15770: fix: prevent phantom <media:unknown> messages from Signal protocol events

by joetomasone open 2026-02-13 21:28 View on GitHub →
size: XS
## Problem Users receive phantom `<media:unknown>` messages on Signal that they didn't send. These appear as real messages from the bot, causing confusion (and unwanted notification chimes on phone, watch, and desktop). ## Root Cause When Signal delivers protocol-level events (profile key updates, group metadata changes, disappearing message timer changes), signal-cli may represent them as messages with an `attachments` array where entries have no `contentType`. `mediaKindFromMime()` returned `"unknown"` for null/undefined mime types. Since `"unknown"` is truthy, the Signal event handler's `if (kind)` check passed and produced `<media:unknown>` placeholders. These became the message body and were delivered to users as phantom messages. ## Fix Return `undefined` instead of `"unknown"` from `mediaKindFromMime()` when the mime type is null/undefined. This causes downstream `if (kind)` guards to correctly skip placeholder generation, and existing `if (!bodyText) return` checks filter out the empty messages. The `"unknown"` return value is preserved for unrecognized but **present** mime types (e.g. `application/x-custom`), which is correct behavior. ## Impact - **Signal**: Phantom `<media:unknown>` messages no longer delivered - **All channels**: Consistent behavior — missing mime ≠ unknown mime - **No breaking changes**: All callers already use truthy checks or switch/default patterns that handle `undefined` gracefully ## Testing Verified all callers of `mediaKindFromMime` across Signal, Telegram, iMessage, Discord, and web media handlers — all use `if (kind)`, switch/default, or optional chaining patterns that correctly handle `undefined`. <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This change updates `mediaKindFromMime()` to return `undefined` (instead of `"unknown"`) when the MIME type is missing, so downstream placeholder generation can correctly skip attachments that lack `contentType` (e.g., Signal protocol-level events). The main correctness concern is that not all wrappers/callers were updated for the new return type: `src/media/mime.ts` still exposes `kindFromMime(): MediaKind` while directly returning the now-optional result. That creates a type contract break and can leak `undefined` into code paths that assume a concrete `MediaKind`. <h3>Confidence Score: 3/5</h3> - This PR is close to safe but has a concrete type/contract break that should be fixed before merging. - The behavioral change in `mediaKindFromMime` looks targeted and aligns with the described Signal issue, but at least one wrapper (`kindFromMime`) still claims to return a non-optional `MediaKind` while forwarding a now-optional value. That’s likely to fail TS compilation or lead to incorrect assumptions in callers. - src/media/mime.ts <sub>Last reviewed commit: 6a573c5</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs