Skip to content

fix(play): log Spotify errors, add fallback chain to executePlayAtTop, fix Last.fm artist parsing - #590

Merged
LucasSantana-Dev merged 4 commits into
mainfrom
fix/play-spotify-error-logging
Apr 13, 2026
Merged

LucasSantana-Dev merged 4 commits into
mainfrom
fix/play-spotify-error-logging

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented Apr 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Log the actual Spotify error in /play primary search catch block so we can diagnose why SpotifyExtractor fails
  • Add Spotify → YouTube → SoundCloud fallback chain to executePlayAtTop (/playnow, /playtop) — previously had no fallback at all
  • Fix Last.fm artist field parsing: user.getrecenttracks returns artist: { '#text': '...' } not { name: '...' }, causing TypeError: Cannot read properties of undefined (reading 'toLowerCase') that broke all Last.fm seed track loading

Root cause of same-song loop

The Last.fm TypeError caused getLastFmSeedTracks to always return [], disabling the personalized seed diversity entirely. Without Last.fm seeds, autoplay falls back to searching YouTube for "Don't Look Back In Anger" variants and keeps recommending the same Oasis tracks.

Test plan

  • PR CI passes
  • Deploy v2.6.108
  • Check production logs: "Failed to load Last.fm seed tracks" should disappear
  • Check production logs: Spotify errors now show actual error message
  • Autoplay queues diverse songs instead of Oasis variants

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved music playback reliability with automatic fallback to alternative search engines when the primary search fails.
    • Enhanced handling of artist data from Last.fm to support different response formats, ensuring consistent track information extraction.
    • Fixed autoplay functionality to exclude tracks with missing artist or title information, preventing playback errors.
  • Tests

    • Added test coverage for alternative Last.fm artist data formats and malformed track handling.

@vercel

vercel Bot commented Apr 13, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lucky Ready Ready Preview, Comment Apr 13, 2026 5:35pm

@coderabbitai

coderabbitai Bot commented Apr 13, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@LucasSantana-Dev has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 54 minutes and 4 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 54 minutes and 4 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 091a4745-6414-4498-a3a4-b8bb5c20974b

📥 Commits

Reviewing files that changed from the base of the PR and between bc0e1f3 and 7039e56.

📒 Files selected for processing (1)
  • packages/bot/src/functions/music/commands/play/queryUtils.spec.ts
📝 Walkthrough

Walkthrough

This pull request enhances the music play command's fallback behavior when searches fail, adds defensive artist parsing in Last.fm API responses to handle objects with #text fields, and filters out malformed tracks with missing artist or title fields from autoplay seed generation.

Changes

Cohort / File(s) Summary
Play Command Fallback Logic
packages/bot/src/functions/music/commands/play/index.ts, packages/bot/src/functions/music/commands/play/queryUtils.ts
Implemented multi-stage fallback flow in queryUtils.ts: if initial play attempt fails and searchEngine is not AUTO, retry with YOUTUBE_SEARCH, then SOUNDCLOUD_SEARCH. Updated index.ts logging to include caught primaryError in warnLog payload and removed inline comments describing fallback behavior.
Last.fm API Artist Parsing
packages/bot/src/lastfm/lastFmApi.ts, packages/bot/src/lastfm/lastFmApi.spec.ts
Enhanced getRecentTracks and getLovedTracks to defensively extract artist field by prioritizing #text before name when artist is an object. Added two new test cases validating correct artist extraction from API responses where artist contains #text instead of name.
Autoplay Seed Track Filtering
packages/bot/src/utils/music/autoplay/lastFmSeeds.ts, packages/bot/src/utils/music/autoplay/lastFmSeeds.spec.ts
Modified deduplicateTracks to filter out malformed track entries missing artist or title before deduplication processing. Added unit test validating that tracks with undefined artist or title are excluded from seed results.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #522: Modifies the same play command files (play/index.ts, queryUtils.ts) to implement search engine fallback and resolve searchEngine selection logic.
  • PR #538: Implements similar multi-stage fallback chain (primary → YouTube → SoundCloud/AUTO) and updates play command logging with error details.
  • PR #567: Updates Last.fm seed and API code with defensive artist parsing and track filtering logic, directly addressing similar data validation concerns.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses all three main changes: error logging for Spotify/play issues, addition of fallback chain to executePlayAtTop, and Last.fm artist parsing fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/play-spotify-error-logging

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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

🧹 Nitpick comments (2)
packages/bot/src/functions/music/commands/play/index.ts (1)

178-183: Inconsistent error logging in YouTube fallback.

The YouTube fallback warning at line 182 only logs { query } but omits the actual error. This is inconsistent with:

  • The primary error logging at line 170 which includes error: String(primaryError)
  • The equivalent code in queryUtils.ts (line 132) which includes error: String(youtubeError)

For consistent debugging, consider adding the error here as well.

♻️ Suggested fix
                     warnLog({
                         message:
                             'YouTube search failed, falling back to SoundCloud',
-                        data: { query },
+                        data: { query, error: String(youtubeError) },
                     })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/play/index.ts` around lines 178 -
183, The YouTube fallback catch block currently calls warnLog without including
the caught youtubeError; update the catch in the play command (the catch
handling youtubeError near the warnLog call) to add the error to the logged data
(e.g., include error: String(youtubeError) or youtubeError.message) so it
matches the primary error logging and the implementation in queryUtils.ts for
consistent debugging.
packages/bot/src/utils/music/autoplay/lastFmSeeds.ts (1)

33-33: Consider trimming before the empty-field guard.

Line 33 filters falsy values, but " " still passes. Using trimmed checks avoids whitespace-only seeds entering dedupe.

Proposed tweak
-        if (!t.artist || !t.title) return false
+        if (!t.artist?.trim() || !t.title?.trim()) return false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/utils/music/autoplay/lastFmSeeds.ts` at line 33, Trim artist
and title fields before the emptiness guard to prevent whitespace-only strings
from passing; update the check around the seed object (where t.artist and
t.title are validated) to use trimmed values (e.g., check t.artist?.trim() and
t.title?.trim()) and use those trimmed values for downstream dedupe/usage in the
functions handling seeds so whitespace-only seeds are rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bot/src/lastfm/lastFmApi.ts`:
- Around line 218-223: The mapper currently assumes t.artist is non-null and
will throw if an item has a nullish artist; update the artist extraction in the
mapping logic (the ternary that checks typeof t.artist === 'string') to first
guard against null/undefined (e.g., check t.artist != null), use optional
chaining/careful type narrowing for object access ((t.artist as Record<string,
string> | null)?.['#text'] ?? (t.artist as any)?.name ?? ''), and ensure this
defensive pattern is applied to both occurrences referenced (the artist
extraction around the ternary and the similar block at lines ~307-310) so a
single malformed entry returns an empty string for artist instead of throwing
and causing the whole call to return [].

---

Nitpick comments:
In `@packages/bot/src/functions/music/commands/play/index.ts`:
- Around line 178-183: The YouTube fallback catch block currently calls warnLog
without including the caught youtubeError; update the catch in the play command
(the catch handling youtubeError near the warnLog call) to add the error to the
logged data (e.g., include error: String(youtubeError) or youtubeError.message)
so it matches the primary error logging and the implementation in queryUtils.ts
for consistent debugging.

In `@packages/bot/src/utils/music/autoplay/lastFmSeeds.ts`:
- Line 33: Trim artist and title fields before the emptiness guard to prevent
whitespace-only strings from passing; update the check around the seed object
(where t.artist and t.title are validated) to use trimmed values (e.g., check
t.artist?.trim() and t.title?.trim()) and use those trimmed values for
downstream dedupe/usage in the functions handling seeds so whitespace-only seeds
are rejected.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 480f26a8-f8b8-46b6-b335-00d02600f644

📥 Commits

Reviewing files that changed from the base of the PR and between 9559123 and bc0e1f3.

📒 Files selected for processing (6)
  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
  • packages/bot/src/lastfm/lastFmApi.spec.ts
  • packages/bot/src/lastfm/lastFmApi.ts
  • packages/bot/src/utils/music/autoplay/lastFmSeeds.spec.ts
  • packages/bot/src/utils/music/autoplay/lastFmSeeds.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: SonarCloud Scan
  • GitHub Check: Quality Gates
🔇 Additional comments (4)
packages/bot/src/functions/music/commands/play/queryUtils.ts (1)

109-141: LGTM! Fallback chain implementation is correct.

The nested try-catch structure properly cascades through search engines (primary → YouTube → SoundCloud), and error logging captures each failure stage. The condition searchEngine !== QueryType.AUTO correctly avoids redundant retries when AUTO is already used. This aligns with the established fallback pattern in queueManipulation.ts.

One observation: this fallback logic is nearly identical to the implementation in index.ts (lines 154-192). Consider extracting to a shared helper in a future refactor to reduce duplication.

packages/bot/src/functions/music/commands/play/index.ts (1)

170-170: LGTM! Primary error now captured in logs.

Adding error: String(primaryError) addresses the PR objective of logging Spotify errors for better diagnosis of SpotifyExtractor failures.

packages/bot/src/lastfm/lastFmApi.spec.ts (1)

283-306: Good coverage for real Last.fm artist['#text'] payload shape.

These tests validate the exact response format that caused the regression and protect both recent/loved parsing paths.

Also applies to: 564-582

packages/bot/src/utils/music/autoplay/lastFmSeeds.spec.ts (1)

148-164: Nice regression test for malformed track filtering.

This directly validates the new defensive behavior and protects seed quality.

Comment on lines +218 to +223
artist:
typeof t.artist === 'string'
? t.artist
: ((t.artist as Record<string, string>)['#text'] ??
t.artist.name ??
''),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Harden artist extraction to avoid dropping the entire result set on malformed entries.

Line 221 and Line 308 still assume t.artist is always non-null before property access. If one item has a nullish artist, the mapper throws and the catch path returns [] for the whole call.

Proposed fix
+function extractLastFmArtist(artist: unknown): string {
+    if (typeof artist === 'string') return artist.trim()
+    if (!artist || typeof artist !== 'object') return ''
+    const record = artist as Record<string, unknown>
+    const text =
+        typeof record['#text'] === 'string' ? record['#text'].trim() : ''
+    const name = typeof record.name === 'string' ? record.name.trim() : ''
+    return text || name
+}
+
 export async function getRecentTracks(
@@
             .filter((t) => !t['@attr']?.nowplaying)
             .map((t) => ({
-                artist:
-                    typeof t.artist === 'string'
-                        ? t.artist
-                        : ((t.artist as Record<string, string>)['#text'] ??
-                          t.artist.name ??
-                          ''),
+                artist: extractLastFmArtist(t.artist),
                 title: t.name,
             }))
@@
         return (data.lovedtracks?.track ?? []).map((t) => ({
-            artist:
-                (t.artist as Record<string, string>)['#text'] ??
-                t.artist.name ??
-                '',
+            artist: extractLastFmArtist(t.artist),
             title: t.name,
         }))

Also applies to: 307-310

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/lastfm/lastFmApi.ts` around lines 218 - 223, The mapper
currently assumes t.artist is non-null and will throw if an item has a nullish
artist; update the artist extraction in the mapping logic (the ternary that
checks typeof t.artist === 'string') to first guard against null/undefined
(e.g., check t.artist != null), use optional chaining/careful type narrowing for
object access ((t.artist as Record<string, string> | null)?.['#text'] ??
(t.artist as any)?.name ?? ''), and ensure this defensive pattern is applied to
both occurrences referenced (the artist extraction around the ternary and the
similar block at lines ~307-310) so a single malformed entry returns an empty
string for artist instead of throwing and causing the whole call to return [].

@sonarqubecloud

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit 8f330de into main Apr 13, 2026
12 checks passed
LucasSantana-Dev added a commit that referenced this pull request May 13, 2026
…tTop fallback (#590)

* fix(play): log primary error and add fallback chain to executePlayAtTop

* fix(lastfm): handle #text artist field in recenttracks and lovedtracks

* test(lastfm): add coverage for #text artist format and undefined guard

* test(play): add executePlayAtTop fallback chain coverage
@LucasSantana-Dev
LucasSantana-Dev deleted the fix/play-spotify-error-logging branch May 23, 2026 02:21

This branch was successfully deployed

1 active deployment
Preview — 7039e56d Deployed Apr 13, 2026 by vercel[bot]
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.

1 participant