Repository navigation
feat(twitch): use Promise.allSettled for per-event subscription error logging - #1749
Conversation
… 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
|
Failed to generate code suggestions for PR |
📝 WalkthroughWalkthroughTwitch 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. ChangesEventSub subscription reliability
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
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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
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.
Comment on review thread (PR #1749)Verdict: Issue is valid ✓ Confirmed — Root cause: When Impact: The PR adds per-event error logging but doesn't achieve its goal because the promises never reject. Recommended fix: Make |
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 winCount existing subscriptions as successful.
On a reconnect migration, existing EventSub subscriptions and
subscribedIdsare retained. Every ID is skipped, leavingsuccessCountat 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
📒 Files selected for processing (2)
packages/bot/src/twitch/eventsubClient.tspackages/bot/src/twitch/eventsubSubscriptions.ts
- 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
There was a problem hiding this comment.
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
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}'.
There was a problem hiding this comment.
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
|
🤖 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).
…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 -->



Summary
Promise.allSettled()instead ofPromise.all()in bothsession_welcomehandler andrefreshSubscriptions()Changes
EVENT_LABELSconstant mapping subscription indices to event type namesPromise.all().catch()withPromise.allSettled()+ per-result checkingTwitch EventSub: {event_type} subscription failedVerification
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.allSettledwith per-event error logging. Handles partial failures and prevents false failure logs on reconnect, aligning with Linear #1529.Promise.allwithPromise.allSettledinsession_welcomeandrefreshSubscriptions; log per-event failures usingEVENT_LABELS.subscribeToEventwhen none succeed to enable per-event failure logging.sessionIdin a local const for the welcome handler and normalizeallSettledreason typing.Written for commit e06b7dd. Summary will update on new commits.
Summary by CodeRabbit