Skip to content

feat(twitch): use Promise.allSettled for per-event subscription error logging - #1749

Merged
LucasSantana-Dev merged 23 commits into
mainfrom
fix/1529-twitch-promise-allsettled
Jul 10, 2026
Merged

LucasSantana-Dev merged 23 commits into
mainfrom
fix/1529-twitch-promise-allsettled

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented Jul 9, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Use Promise.allSettled() instead of Promise.all() in both session_welcome handler and refreshSubscriptions()
  • Add per-event error logging to identify which specific subscription failed
  • Gracefully handle partial failures instead of aborting on first failure

Changes

  • Added EVENT_LABELS constant mapping subscription indices to event type names
  • Replaced Promise.all().catch() with Promise.allSettled() + per-result checking
  • Each rejected subscription now logs: Twitch EventSub: {event_type} subscription failed

Verification

Diff reviewed; code changes replace generic error handling with granular per-event logging. Both Promise.all() instances replaced with allSettled().

Closes #1529


Summary by cubic

Switch Twitch EventSub subscriptions to Promise.allSettled with per-event error logging. Handles partial failures and prevents false failure logs on reconnect, aligning with Linear #1529.

  • Bug Fixes
    • Replace Promise.all with Promise.allSettled in session_welcome and refreshSubscriptions; log per-event failures using EVENT_LABELS.
    • Count already-subscribed broadcasters as success; throw in subscribeToEvent when none succeed to enable per-event failure logging.
    • Capture sessionId in a local const for the welcome handler and normalize allSettled reason typing.

Written for commit e06b7dd. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved Twitch event subscription reliability by allowing successful subscriptions to continue even when individual event registrations fail.
    • Added clearer error reporting to identify which event subscription could not be registered or refreshed.
    • Subscription attempts now correctly report a failure when none of the requested broadcaster subscriptions succeed.

LucasSantana-Dev and others added 2 commits July 9, 2026 12:55
… logging

Replace Promise.all() with Promise.allSettled() in both session_welcome
handler and refreshSubscriptions() to handle partial failures gracefully.
Add per-event error logging so we can identify which specific subscription
failed instead of just logging a generic error.

Closes #1529
@github-actions github-actions Bot added the bot label Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Failed to generate code suggestions for PR

@coderabbitai

coderabbitai Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Twitch EventSub registration now records individual subscription failures without aborting sibling registrations. Broadcaster subscription setup also counts successful attempts and throws when no discovered broadcaster can be subscribed.

Changes

EventSub subscription reliability

Layer / File(s) Summary
Event labels and per-subscription failure logging
packages/bot/src/twitch/eventsubClient.ts
session_welcome and refreshSubscriptions() use Promise.allSettled() and log rejected subscriptions with their corresponding EventSub labels.
Aggregate broadcaster subscription failure detection
packages/bot/src/twitch/eventsubSubscriptions.ts
Subscription setup counts successful broadcaster registrations and throws when all attempts fail for a non-empty user list.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TwitchEventSubClient
  participant TwitchAPI
  participant Logger

  TwitchEventSubClient->>TwitchAPI: Register four EventSub subscriptions
  TwitchAPI-->>TwitchEventSubClient: Promise.allSettled results
  loop Each settled result
    alt Subscription rejected
      TwitchEventSubClient->>Logger: Log event label and rejection reason
    end
  end
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements Promise.allSettled in both session_welcome and refreshSubscriptions with per-event labels, matching issue #1529.
Out of Scope Changes check ✅ Passed The extra subscribeToEvent failure rejection supports the requested logging fix and no unrelated changes are evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: switching Twitch EventSub handling to Promise.allSettled with per-event error logging.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1529-twitch-promise-allsettled

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the size/m label Jul 9, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/bot/src/twitch/eventsubClient.ts">

<violation number="1" location="packages/bot/src/twitch/eventsubClient.ts:162">
P2: Per-event failure logging will miss most actual Twitch subscription failures because `subscribeTo*` resolves even when `createSubscription()` returns `false`. Consider returning/throwing failure details from the subscription helpers, or logging the event type at the `createSubscription` failure site, so HTTP/fetch failures are labeled too.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/bot/src/twitch/eventsubClient.ts
LucasSantana-Dev and others added 2 commits July 9, 2026 15:14
this.sessionId is string | null; TS can't carry the narrowing from
the assignment into the nested async IIFE, so the four subscribeTo*
calls failed to typecheck. p.session.id is always a non-null string,
so capture it once and pass the local instead of re-reading the
nullable field inside the closure.
@LucasSantana-Dev
LucasSantana-Dev enabled auto-merge (squash) July 9, 2026 18:57
@LucasSantana-Dev

Copy link
Copy Markdown
Owner Author

Comment on review thread (PR #1749)

Verdict: Issue is valid ✓

Confirmed — subscribeToEvent() never rejects, so the Promise.allSettled per-event handlers never trigger.

Root cause: When createSubscription() returns false (HTTP/network error), the error IS logged at line 164 of eventsubSubscriptions.ts. But subscribeToEvent() silently continues the loop and resolves with void. The promise in eventsubClient.ts line 150 never rejects, so the per-event error log never runs — it's dead code.

Impact: The PR adds per-event error logging but doesn't achieve its goal because the promises never reject.

Recommended fix: Make subscribeToEvent() reject if no subscriptions succeeded, or return a success indicator so the caller knows the whole event subscription failed. This is a minimal one-line fix and aligns with the PR's stated purpose.

LucasSantana-Dev and others added 2 commits July 9, 2026 21:08
Make subscribeToEvent() reject when all subscription attempts fail, so
the Promise.allSettled handler in eventsubClient can log per-event type
failures. Previously the helper always resolved, making the per-event
error logging dead code — HTTP errors were only logged per-request, not
per-event type.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/bot/src/twitch/eventsubSubscriptions.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/bot/src/twitch/eventsubSubscriptions.ts (1)

84-105: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Count existing subscriptions as successful.

On a reconnect migration, existing EventSub subscriptions and subscribedIds are retained. Every ID is skipped, leaving successCount at zero and causing a false per-event failure log despite active subscriptions. The same occurs when one ID is already subscribed and newly discovered IDs fail. Track an existing-or-new successful subscription, rather than only newly created ones.

Proposed fix
-    let successCount = 0
+    let hasSubscribedBroadcaster = false
     for (const broadcasterUserId of userIds) {
-        if (subscribedIds.has(broadcasterUserId)) continue
+        if (subscribedIds.has(broadcasterUserId)) {
+            hasSubscribedBroadcaster = true
+            continue
+        }
         const ok = await createSubscription(
             broadcasterUserId,
             token,
             sessionId,
             clientId,
             type,
             version,
             conditionKey,
         )
         if (ok) {
             subscribedIds.add(broadcasterUserId)
-            successCount++
+            hasSubscribedBroadcaster = true
         }
     }

-    if (successCount === 0 && userIds.length > 0) {
+    if (!hasSubscribedBroadcaster) {
         throw new Error(
             `Twitch EventSub: failed to subscribe to any broadcasters for ${type}`,
         )
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bot/src/twitch/eventsubSubscriptions.ts` around lines 84 - 105,
Update the subscription loop in the event subscription migration logic to count
IDs already present in subscribedIds as successful before skipping them, while
continuing to count newly created subscriptions when createSubscription
succeeds. Use this combined success count for the failure check so retained
subscriptions do not trigger a false error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/bot/src/twitch/eventsubClient.ts`:
- Around line 162-169: The rejected promise reasons in the Promise.allSettled
handlers should not be passed as any to errorLog. In both result-processing
branches around the subscription handling logic, normalize result.reason to
unknown or an Error before assigning it to the error field, preserving the
existing failure context and satisfying type-safe linting.

---

Outside diff comments:
In `@packages/bot/src/twitch/eventsubSubscriptions.ts`:
- Around line 84-105: Update the subscription loop in the event subscription
migration logic to count IDs already present in subscribedIds as successful
before skipping them, while continuing to count newly created subscriptions when
createSubscription succeeds. Use this combined success count for the failure
check so retained subscriptions do not trigger a false error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bd74a72d-239e-494f-af7b-90bf4b70ffa5

📥 Commits

Reviewing files that changed from the base of the PR and between 2e5fd94 and 6facb36.

📒 Files selected for processing (2)
  • packages/bot/src/twitch/eventsubClient.ts
  • packages/bot/src/twitch/eventsubSubscriptions.ts

Comment thread packages/bot/src/twitch/eventsubClient.ts
LucasSantana-Dev and others added 7 commits July 9, 2026 23:38
- Track existing subscriptions as successful to prevent false failure logs on reconnect
- When subscribedIds already contains all broadcasters after session_reconnect,
  the previous check incorrectly threw despite subscriptions being active
- Normalize Promise.allSettled reason type for proper type safety

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Requires human review: Refactors error handling in Twitch EventSub subscriptions. Functional change in a critical integration path; requires human review for correctness and potential side effects.

Re-trigger cubic

LucasSantana-Dev and others added 2 commits July 10, 2026 11:55
The subscription function now throws when all subscription attempts fail and
no broadcasters were pre-subscribed, enabling per-event error logging via
Promise.allSettled in the caller. The test was written before this throwing
behavior was added and expected no throw. Updated to match the correct new
behavior: when all subscriptions fail, the function should throw
'Twitch EventSub: failed to subscribe to any broadcasters for ${type}'.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 1 file (changes from recent commits).

Auto-approved: Switches to Promise.allSettled for per-event error logging in Twitch EventSub; adds partial-failure handling. Safe defensive change with low risk.

Re-trigger cubic

@sonarqubecloud

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit 6691305 into main Jul 10, 2026
44 checks passed
@LucasSantana-Dev
LucasSantana-Dev deleted the fix/1529-twitch-promise-allsettled branch July 10, 2026 17:20
LucasSantana-Dev added a commit that referenced this pull request Jul 10, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>2.34.0</summary>

##
[2.34.0](v2.33.1...v2.34.0)
(2026-07-10)


### Features

* **twitch:** use Promise.allSettled for per-event subscription error
logging ([#1749](#1749))
([6691305](6691305))


### Bug Fixes

* [#1699](#1699)
([eef5aee](eef5aee))
* **backend:** migrate webhooks to use canonical timingsafekey
comparison
([#1747](#1747))
([eef5aee](eef5aee))
* **backend:** wrap lastfm routes with asynchandler
([#1726](#1726))
([ce51d86](ce51d86))
* **batch-move:** graceful attachment-fetch degradation + mid-loop
client re-check
([#1750](#1750))
([f21a0ce](f21a0ce))
* **bot:** approve @discordjs/opus install script — P0 music playback
outage ([#1757](#1757))
([9d894e4](9d894e4))
* **ci:** add missing packages field to pnpm-workspace.yaml
([#1760](#1760))
([a4c585d](a4c585d))
* **ci:** remove pnpm shim from bundle-size workflow
([#1759](#1759))
([eaf676f](eaf676f))
* **deploy:** increase validation timeout to 10min
([#1743](#1743))
([07891ec](07891ec))
* **docker:** copy+chown [@prisma](https://github.com/prisma) engines in
production-backend — P0 deploy pipeline blocker
([#1758](#1758))
([a70d0e8](a70d0e8))
* eliminate mock state pollution in bot tests and remove resetMocks
config ([#1741](#1741))
([2e5fd94](2e5fd94))
* **frontend:** prevent state updates after unmount
([#1748](#1748))
([f4e7c45](f4e7c45))
* pin file-type to resolve CI flake
[#1740](#1740)
([#1753](#1753))
([6b8e527](6b8e527))
* reduce Jest maxWorkers and add DB pool config for test stability
([#1751](#1751))
([cfead33](cfead33))
* use fake timers in ReminderService.spec to prevent race condition
([#1745](#1745))
([ba2908c](ba2908c))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
LucasSantana-Dev added a commit that referenced this pull request Jul 10, 2026
…1762)

Closes the real gaps from gap analysis against web-app issue #130
(Twitch notification pipeline already existed): Twitch poll normalized
to 5 min; YouTube live detection via a single quota-safe search.list
(30-min interval = 4.8k units/day vs 14.4k that the spec'd 10-min would
burn against the 10k/day free quota — deviation documented in code);
posted-notification tracking with auto-delete on stream end or 4h TTL
(in-memory; restart-orphan limitation documented — Prisma model
deliberately NOT added); exponential backoff honoring Retry-After on
429/5xx for both APIs. New env (optional): YOUTUBE_API_KEY +
YOUTUBE_CHANNEL_ID — absent disables YouTube path with a startup log.
27/27 spec tests. eventsub files untouched (PR #1749 owns them).
Cross-repo: Criativaria-Projects/web-app#130.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds YouTube live polling and 4h TTL cleanup to the live notification
service, with exponential backoff and a 5-minute Twitch poll. Uses a
quota-safe 30-minute YouTube interval (single `search.list`) with
`crypto.randomInt` jitter; aligns with web-app #130.

- **New Features**
  - Twitch polling set to 5 minutes.
- YouTube live detection via one `search.list` every 30 minutes;
disabled without `YOUTUBE_API_KEY` and `YOUTUBE_CHANNEL_ID`.
- De-dup per stream/broadcast and track posted messages; auto-delete
after 4 hours; soft-fail on missing/already-deleted messages.
- Exponential backoff for Twitch and YouTube requests, respecting
Retry-After on 429/5xx.

- **Bug Fixes**
- Fixed Retry-After parsing (supports seconds and HTTP-date) and
improved backoff resilience.
- Start decouples YouTube from Twitch env; added tests for YouTube-only
start, Retry-After variants, and empty search results.
- Repaired a duplicate test block; deflaked Retry-After HTTP-date timing
test.

<sup>Written for commit 10d4bf7.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/LucasSantana-Dev/Lucky/pull/1762?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added YouTube live broadcast polling and notifications alongside
Twitch.
* Notifications now use platform-specific embeds, with optional role
mentions.
* **Bug Fixes**
* Improved polling reliability with independent Twitch/YouTube
intervals, re-entrancy guards, and backoff with `Retry-After` handling.
* Prevented duplicate notifications and added TTL-based cleanup for
stale messages (including safe handling when deletions fail).
* **Tests**
* Expanded coverage for edge cases, retry/backoff behavior, and
lifecycle start/stop logic.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This was referenced Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(twitch): use Promise.allSettled for per-event subscription error logging

1 participant