Describe the bug
Every Discord notification is sent to the webhook twice. The Discord-specific handler in NotificationService.Webhooks.cs sends the notification (multipart with cover attachment, or plain JSON), but does not return afterward — execution falls through to the generic webhook sender at the bottom of SendNotificationAsync, which posts a second copy of the same notification.
Depending on configuration, this shows up two different ways:
- Duplicate messages — when the generic payload is valid, the Discord channel receives the same notification twice.
- Spurious 400 errors in the log — when the configured base URL is not an absolute
http(s) URL (e.g. the default UrlBase of "/" in config.json), the generic payload's embed thumbnail is a relative path (/api/v1/images/<asin>), which the Discord API rejects:
[INF] Sending Generic POST to https://discord.com/api/webhooks/... with body: {"content":"Dracula by Bram Stoker has been added", ... "thumbnail":{"url":"/api/v1/images/B0078PA1OA"} ...}
[WRN] Failed to send notification to https://discord.com/api/webhooks/...: BadRequest - {"embeds": ["0"]}
The log makes delivery look broken even though the first (Discord-specific) send already succeeded — this cost me a while when setting up Discord notifications, since I saw a 400 on every book-added event.
The NTFY handler has the same fall-through, so NTFY targets also receive the notification twice (once as a proper NTFY message with Title/Priority/Tags headers, once as generic JSON).
The Pushover and Telegram handlers already return after handling, so they are unaffected.
To Reproduce
- Configure a Discord webhook URL as the notification target (default
UrlBase of "/").
- Trigger any notification with an image, e.g. add a book to the library (
book-added).
- Observe the log: the Discord-specific send succeeds (message appears in the channel with cover attachment), followed by a
Sending Generic POST to the same webhook that fails with BadRequest - {"embeds": ["0"]}.
Expected behavior
One notification per event per target: the service-specific handler should be terminal for URLs it handles, with no second post through the generic sender.
Environment
- Version: 1.2.2 (canary)
- OS: Windows 11 (dev build from source), also applies to current
canary sources
Additional context
I have a fix ready (two return statements plus regression tests asserting exactly one HTTP request per Discord/NTFY notification, verified failing before the fix) — PR incoming.
Describe the bug
Every Discord notification is sent to the webhook twice. The Discord-specific handler in
NotificationService.Webhooks.cssends the notification (multipart with cover attachment, or plain JSON), but does notreturnafterward — execution falls through to the generic webhook sender at the bottom ofSendNotificationAsync, which posts a second copy of the same notification.Depending on configuration, this shows up two different ways:
http(s)URL (e.g. the defaultUrlBaseof"/"inconfig.json), the generic payload's embed thumbnail is a relative path (/api/v1/images/<asin>), which the Discord API rejects:The log makes delivery look broken even though the first (Discord-specific) send already succeeded — this cost me a while when setting up Discord notifications, since I saw a 400 on every
book-addedevent.The NTFY handler has the same fall-through, so NTFY targets also receive the notification twice (once as a proper NTFY message with Title/Priority/Tags headers, once as generic JSON).
The Pushover and Telegram handlers already
returnafter handling, so they are unaffected.To Reproduce
UrlBaseof"/").book-added).Sending Generic POSTto the same webhook that fails withBadRequest - {"embeds": ["0"]}.Expected behavior
One notification per event per target: the service-specific handler should be terminal for URLs it handles, with no second post through the generic sender.
Environment
canarysourcesAdditional context
I have a fix ready (two
returnstatements plus regression tests asserting exactly one HTTP request per Discord/NTFY notification, verified failing before the fix) — PR incoming.