BUG: Sonarr and Radarr webhook test buttons report failure after #62 #71

Closed
opened 2026-05-28 17:52:46 +01:00 by Gandalf · 1 comment
Owner

Related Issues:

  • #62 (Webhook reliability — replay key redesign)
  • #70 (Ombi webhook regression — fixed)

Priority: High
Status: Open


Description

After the changes in #62, the Sonarr and Radarr webhook test buttons now report failure, even though real webhooks may still function.

Clicking the test button results in the webhook being rejected as a duplicate.


Root Cause

The improved replay protection in #62 introduced a 4-parameter isReplay() function:

function isReplay(eventType, instanceName, eventDate, contentId)

In the Sonarr and Radarr handlers, contentId is extracted as:

const contentId = req.body.downloadId || req.body.series?.id || req.body.movie?.id || null;

Problem:
Webhook test events sent by the UI use eventType: "Test" and do not contain downloadId, series.id, or movie.id.

This results in contentId = null.

The replay key becomes:

Test:<instanceName>::<eventDate>

When the test button is clicked more than once within the 5-minute replay window (or when eventDate has second-level precision), the key collides → isReplay() returns true → the test event is rejected as a duplicate.

This is a side effect of the stricter replay protection introduced in #62.


Evidence from Current Code

Sonarr Handler:

const contentId = req.body.downloadId || req.body.series?.id || null;
if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) { ... }

Radarr Handler:

const contentId = req.body.downloadId || req.body.movie?.id || null;
if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) { ... }

Typical Test Payload (from UI):

{
  "eventType": "Test",
  "instanceName": "main",
  "date": "2026-05-28T17:45:00Z"
}

→ No media identifiers → contentId = null → false duplicate detection.


Impact

  • Sonarr and Radarr webhook test buttons are unusable.
  • Users cannot easily verify webhook configuration.
  • Reduces confidence in the webhook system after the reliability improvements in #62.
  • Affects both real test usage and automated testing.

How to Resolve (Recommended Fix)

Best Solution: Skip replay protection for eventType === "Test" events.

Patch for server/routes/webhook.js

Add the following check in both the Sonarr and Radarr handlers, right after payload validation:

// Skip replay protection for Test events (they are intentionally repeatable)
if (eventType === "Test") {
    logToFile(`[Webhook] ${serviceType} Test event received — skipping replay protection`);
} else if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) {
    logToFile(`[Webhook] ${serviceType} duplicate event ignored: ${eventType} @ ${eventDate}`);
    return res.status(200).json({ received: true, duplicate: true });
}

Full Recommended Replacement (Sonarr handler example):

const validation = validatePayload(req.body);
if (!validation.valid) {
    return res.status(400).json({ error: validation.reason });
}

const { eventType, instanceName, eventDate } = validation;
const contentId = req.body.downloadId || req.body.series?.id || null;

// Skip replay protection for Test events
if (eventType === "Test") {
    logToFile(`[Webhook] Sonarr Test event received — skipping replay protection`);
} else if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) {
    logToFile(`[Webhook] Sonarr duplicate event ignored: ${eventType} @ ${eventDate}`);
    return res.status(200).json({ received: true, duplicate: true });
}

Apply the same pattern to the Radarr handler.


Suggested Commit Message

fix(webhooks): skip replay protection for Test events (closes #71)

Suggested Labels

  • Kind/Bug
  • Priority: High
  • Area/Webhooks
  • Regression

Cross-References

  • Caused by stricter replay logic in #62
  • Similar to the Ombi regression fixed in #70
  • Affects webhook test functionality used by many users
**Related Issues:** - #62 (Webhook reliability — replay key redesign) - #70 (Ombi webhook regression — fixed) **Priority:** High **Status:** Open --- ## Description After the changes in **#62**, the **Sonarr and Radarr webhook test buttons** now report failure, even though real webhooks may still function. Clicking the test button results in the webhook being rejected as a duplicate. --- ## Root Cause The improved replay protection in #62 introduced a 4-parameter `isReplay()` function: ```js function isReplay(eventType, instanceName, eventDate, contentId) ``` In the Sonarr and Radarr handlers, `contentId` is extracted as: ```js const contentId = req.body.downloadId || req.body.series?.id || req.body.movie?.id || null; ``` **Problem:** Webhook **test events** sent by the UI use `eventType: "Test"` and **do not contain** `downloadId`, `series.id`, or `movie.id`. This results in `contentId = null`. The replay key becomes: ``` Test:<instanceName>::<eventDate> ``` When the test button is clicked more than once within the 5-minute replay window (or when `eventDate` has second-level precision), the key collides → `isReplay()` returns `true` → the test event is rejected as a duplicate. This is a **side effect** of the stricter replay protection introduced in #62. --- ## Evidence from Current Code **Sonarr Handler:** ```js const contentId = req.body.downloadId || req.body.series?.id || null; if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) { ... } ``` **Radarr Handler:** ```js const contentId = req.body.downloadId || req.body.movie?.id || null; if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) { ... } ``` **Typical Test Payload (from UI):** ```json { "eventType": "Test", "instanceName": "main", "date": "2026-05-28T17:45:00Z" } ``` → No media identifiers → `contentId = null` → false duplicate detection. --- ## Impact - Sonarr and Radarr webhook test buttons are unusable. - Users cannot easily verify webhook configuration. - Reduces confidence in the webhook system after the reliability improvements in #62. - Affects both real test usage and automated testing. --- ## How to Resolve (Recommended Fix) **Best Solution:** Skip replay protection for `eventType === "Test"` events. ### Patch for `server/routes/webhook.js` Add the following check in **both** the Sonarr and Radarr handlers, right after payload validation: ```js // Skip replay protection for Test events (they are intentionally repeatable) if (eventType === "Test") { logToFile(`[Webhook] ${serviceType} Test event received — skipping replay protection`); } else if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) { logToFile(`[Webhook] ${serviceType} duplicate event ignored: ${eventType} @ ${eventDate}`); return res.status(200).json({ received: true, duplicate: true }); } ``` ### Full Recommended Replacement (Sonarr handler example): ```js const validation = validatePayload(req.body); if (!validation.valid) { return res.status(400).json({ error: validation.reason }); } const { eventType, instanceName, eventDate } = validation; const contentId = req.body.downloadId || req.body.series?.id || null; // Skip replay protection for Test events if (eventType === "Test") { logToFile(`[Webhook] Sonarr Test event received — skipping replay protection`); } else if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) { logToFile(`[Webhook] Sonarr duplicate event ignored: ${eventType} @ ${eventDate}`); return res.status(200).json({ received: true, duplicate: true }); } ``` Apply the same pattern to the Radarr handler. --- ## Suggested Commit Message ``` fix(webhooks): skip replay protection for Test events (closes #71) ``` --- ## Suggested Labels - `Kind/Bug` - `Priority: High` - `Area/Webhooks` - `Regression` --- ## Cross-References - Caused by stricter replay logic in **#62** - Similar to the Ombi regression fixed in **#70** - Affects webhook test functionality used by many users
Gandalf added the Kind/Bug
Priority
High
2
Area/Webhooks
labels 2026-05-28 17:52:46 +01:00
Author
Owner

Resolved in commit 06818db.

Resolved in commit 06818db.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Gandalf/sofarr#71