Skip to content

Fix search and playlist parsing crashes from Spotify API nulls and duplicate items - #734

Open
Kinonear wants to merge 1 commit into
jpochyla:mainfrom
Kinonear:main
Open

Fix search and playlist parsing crashes from Spotify API nulls and duplicate items#734
Kinonear wants to merge 1 commit into
jpochyla:mainfrom
Kinonear:main

Conversation

@Kinonear

Copy link
Copy Markdown

This PR fixes the crashes caused by recent Spotify Web API changes. It resolves the duplicate field "items" deserialization conflict on /me/playlists and search results, and implements a null-safe deserializer deserialize_ignore_nulls for Page to handle unavailable or region-locked tracks.

@jacksongoode

jacksongoode commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

I believe this might have already been addressed by 6267ec1 were you targeting the same issue?

@Kinonear

Copy link
Copy Markdown
Author

Hi @jacksongoode,

Thanks for the quick response!

While commit 6267ec1 does handle nulls specifically for playlists in search results, this PR targets two broader issues that remain unaddressed in that commit:

The track_count field in Playlist (playlist.rs) currently has #[serde(rename = "items")]. Because of this, deserializing Page<Playlist> (such as when fetching the sidebar playlists /v1/me/playlists or searching) throws a serde_json duplicate field collision. Changing the primary serialization mapping of track_count back to "tracks" resolves this and completely restores the sidebar.

Instead of manually wrapping list elements in Option<T> and flattening them in every separate endpoint's response mapping, our implementation updates the generic Page<T> struct inside utils.rs with deserialize_ignore_nulls. This protects all lists (saved tracks, albums, show episodes, etc.) across the entire application from crashing when Spotify returns null for a region-locked or deleted item.

Additionally, this PR updates the deserialize_track_count helper in playlist.rs to safely handle cases where the entire tracks block is null or omitted.

Let me know what you think!

@jacksongoode

jacksongoode commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Is there a way to avoid making a deserialize_ignore_nulls? It's only used in one place now it would be useful if this was common, but there might be a more inline way of avoiding this helper?

Also I believe builds fail.

@Kinonear

Kinonear commented Jul 2, 2026

Copy link
Copy Markdown
Author

Hi @jacksongoode,

Regarding the build failure, it looks like an environmental cross-compilation issue on the aarch64-unknown-linux-gnu target. The pre-build docker script crashed during dpkg configuration for python3.12-minimal due to a QEMU emulation mismatch (Exec format error) on the runner. This happens before the Rust compiler even starts compiling Psst. The standard Windows, macOS, and x86_64 Linux builds all compiled successfully, confirming the Rust code is syntactically sound.

To your question about avoiding the deserialize_ignore_nulls helper:

Because Rust/Serde does not support inline closure-style deserializers within #[serde(deserialize_with)], a separate helper function is the standard, idiomatic way to handle custom mapping logic.

If we were to avoid the helper entirely, the only other inline option in Serde is to change the field type of Page<T>::items from Vector<T> to Vector<Option<T>>. However, Page<T> is the core paging structure used for almost every response array across the entire application (playlists, artists, albums, search, episodes, etc.). Changing the type to Option would force us to modify dozens of other files across the codebase to manually unwrap/flatten the options.

By keeping this small 6-line helper inside utils.rs, we centralize null-filtering at the deserialization layer. This keeps the rest of the codebase clean, type-safe, and globally protected against similar null array items from Spotify's API in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants