Repository navigation
Where the Spotify plugin has fallen behind the Web API #35
robert-dean
started this conversation in
Ideas
Replies: 1 comment
|
Status, 2026-10-23. Items 4 and 6 are done (#759).
Still open: item 3, the scopes, which stays sequenced with #19 behind a live check of |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Written: 2026-08-09, from a call-by-call read of
plugins/spotifyagainst the live reference andthe February, March, May and July 2026 changelogs.
State of the tree: the plugin absorbed the February 2026 round where it broke things loudly
(
/playlists/{id}/items, theitem/trackdual read,permissionson a listing, 403 classified asforbiddenrather thanauth). What is left is the half of that round that breaks nothing until itdoes. Items 1 and 2 were fixed the same day this was written; 3 through 6 are still open.
Companion to spotify-listening-profile, which covers data the
grant already pays for and the plugin never asks for. This file is the opposite: calls the plugin
makes today that the API has moved underneath.
1.
The search limit clamp is wrong, and it is one line from firingFixed 2026-08-09This was a bug, not deferred design, and was fixed on the spot rather than left here. Recorded
because the reasoning is worth keeping and because it is why
clampLimitnow has a sibling.clampLimitin spotify.mapping.ts clamps toMaxInt<50>and was shared by all three catalog calls, withsearchTracksfeeding it straight intosearch().February cut the search endpoint's
limitto max 10, default 5, leaving the other two call sitesalone:
/me/playlistsstill takes 50, and/me/top/{type}also takes 50. So one shared clamp couldno longer serve all three.
It was not failing only by luck.
PER_PROVIDER_LIMITinprovider.search.ts was exactly
10when thiswas written and is
25today, still under the ceiling. Acaller asking for 11 got a 400,
SpotifyResponseValidatorturned it intoupstream, and the tooldropped that provider from the results without failing the search, by design. The symptom would have
been a model that quietly could not see the Spotify half of the library.
What landed: a separate
clampSearchLimitcapped at 10, rather than loweringclampLimit, whichwould have needlessly halved playlist paging. Plus
clampSearchOffsetfor search's own 1000-rowpaging ceiling, which
searchTrackshad been passing through unclamped.2.
Omitting the limit means five results now, not twentyFixed 2026-08-09Same call site, separate problem, no error to notice: when
options?.limitwasundefinedthe pluginsent nothing and took Spotify's default, which dropped from 20 to 5 in the same round.
This one had teeth beyond the count. Every writer inherits the rule that a break names only records
the station can play, and
search_catalogis how that is checked, so a default that silently went toa quarter depth narrowed what the DJ was permitted to say about the Spotify half of the library.
What landed:
clampSearchLimit(undefined)returns 10 rather than passingundefinedthrough, sothe plugin always states its own depth and the next time Spotify moves that default it does not
quietly follow.
3. One scope in the manifest unlocks nothing (the other one is read)
user-read-emailanduser-read-privateinspotify.manifest.ts exist for
email,countryandproductonGET /me. February removed all three from the response, along withexplicit_contentand
followers.testConnectionusesdisplay_nameandgetCurrentUserIdusesid, and bothsurvived.
Corrected 2026-10-10: this section used to say nothing reads either scope, and that stopped being
true on 2026-08-16 (c8d12ee).
explicitFilterNoticeinspotify.mapping.ts
reads
explicit_content.filter_enabledandfilter_lockedfromGET /me, intestConnectionandagain in the plugin's status, to warn that the account may refuse explicit records. The field is
gated by
user-read-private, and the plugin already reads it defensively because responses arrivepartial. So
user-read-emailcan go anduser-read-privatestays, unless a live call showsexplicit_contentis gone for good, in which case the notice is dead code and both go together.Make that call before changing the manifest.
The sequencing for this lives in listening-loop, which batches it with
adding
user-read-recently-playedso the operator re-consents once rather than three times. Do notship it on its own.
4. The SDK is a museum, and one of its methods is now a trapdoor
getPlaylistTracksalready works around@spotify/web-api-ts-sdkpredating the/itemsrename. Theproblem is wider than that one path.
February removed the batch endpoints
GET /tracks,/albumsand/artistsoutright, and the SDK'stracks.getis overloaded: a string hitstracks/{id}, an array hits the removedtracks?ids=.getTrackonly ever passes a string, so it is correct today. But the removed shape is one argumentaway and would look like an obvious optimisation to whoever next needs several tracks at once. The
same package still ships
audioFeaturesandaudioAnalysis, for endpoints removed in 2024.Cheapest durable fix is a comment at the
getTrackcall site naming the overload as removed. Thelarger question, worth asking once rather than per incident: whether the SDK is still earning its
place, given the plugin already supplies its own
RequestImplementation, its own auth strategy, itsown response validator, and now hand-rolls the endpoints the SDK has not caught up with.
5.
account_idand the two jobs one cache is doingMay 2026 added
account_idtoGET /me: public, immutable, pseudonymous, and recommended overidfor linking an account to another service.
getCurrentUserIdin spotify.plugin.ts cachesprofile.idand feeds it to two callers that do not obviously want the same field:mapPlaylist, comparing againstplaylist.owner.id.idis correcthere and
account_idwould be wrong.resolveStreamUrl. Unverified which field the accesspointexpects.
So this is not a rename. It is a question about whether one cached value should be serving both, and
it needs a live check against the accesspoint before anything is changed. It becomes urgent the first
time something persists a Spotify identity, because
idis not guaranteed stable andaccount_idis.
6. Development mode now requires the owner's Premium subscription
Operator-facing rather than code. Since February the owner of a development-mode app must hold an
active Premium subscription and the app stops working if it lapses, alongside the five-authorized-user
cap.
The failure mode is a station that has worked for months going 403 one morning for a reason nowhere
in this repo explains.
upstreamReasoninspotify.fetch.ts will surface Spotify's own sentence,
which is exactly why that extractor exists, but the plugin's setup copy should say it up front.
July 2026 eased the other half of the same round: Client IDs per developer went from 1 to 25 and the
quota is now counted per developer account rather than per Client ID.
What was checked and is fine
Worth recording so the next pass does not re-verify it:
GET /me/playlistsis current and notdeprecated (max limit 50, offset 100,000, and its items still carry
owner,collaborativeand bothitems.totaland the deprecatedtracks.total, both of whichmapPlaylistreads). The playerendpoints the steer half uses are all current.
external_idswas removed in February and revertedin March, so
mapTrackreadingisrcis safe.display_namesurvived. The removedGET /users/{id}/playlistsis not a path this plugin takes.Sources
Moved from
docs/todo/spotify-api-currency.md. The file as it last stood: https://github.com/robert-dean/deadair/blob/71e431d45ce7b35a189c7d226832ec4a6247e9dc/docs/todo/spotify-api-currency.mdAll reactions