[GH-ISSUE #525] JSON parse error because Spotify returns wrong data #167

Closed
opened 2026-02-27 20:23:31 +03:00 by kerem · 12 comments
Owner

Originally created by @davidemarcoli on GitHub (May 24, 2025).
Original GitHub issue: https://github.com/ramsayleung/rspotify/issues/525

Describe the bug
A clear and concise description of what the bug is.

The spotify returns wrong data which isn't being handled and an exception is thrown.

To Reproduce
Steps to reproduce the behavior:

  1. Get this playlist from the api: 0hnM986qSkpI9YSFwpl96E

Expected behavior
A clear and concise description of what you expected to happen.

It can parse the response without exception

Log/Output data
If applicable, add log data or output data to help explain your problem.

data did not match any variant of untagged enum PlayableItem at line 1 column 86707

Additional context
Add any other context about the problem here.

Playlist which it fails for: https://open.spotify.com/playlist/0hnM986qSkpI9YSFwpl96E/

Example of an item:
https://open.spotify.com/episode/60XIexbGJJYcc2R5idt8p4
The api returns track=true and episode=false, which obviously is not true

Originally created by @davidemarcoli on GitHub (May 24, 2025). Original GitHub issue: https://github.com/ramsayleung/rspotify/issues/525 **Describe the bug** A clear and concise description of what the bug is. The spotify returns wrong data which isn't being handled and an exception is thrown. **To Reproduce** Steps to reproduce the behavior: 1. Get this playlist from the api: 0hnM986qSkpI9YSFwpl96E **Expected behavior** A clear and concise description of what you expected to happen. It can parse the response without exception **Log/Output data** If applicable, add log data or output data to help explain your problem. `data did not match any variant of untagged enum PlayableItem at line 1 column 86707` **Additional context** Add any other context about the problem here. Playlist which it fails for: https://open.spotify.com/playlist/0hnM986qSkpI9YSFwpl96E/ Example of an item: https://open.spotify.com/episode/60XIexbGJJYcc2R5idt8p4 The api returns track=true and episode=false, which obviously is not true
kerem 2026-02-27 20:23:31 +03:00
Author
Owner

@ramsayleung commented on GitHub (May 26, 2025):

Which API you were trying to call to get the playlist?

<!-- gh-comment-id:2908344227 --> @ramsayleung commented on GitHub (May 26, 2025): Which API you were trying to call to get the playlist?
Author
Owner

@davidemarcoli commented on GitHub (May 26, 2025):

I used the playlist_items_manual function.
Meaning the following spotify endpoint: https://api.spotify.com/v1/playlists/0hnM986qSkpI9YSFwpl96E/tracks

<!-- gh-comment-id:2908742388 --> @davidemarcoli commented on GitHub (May 26, 2025): I used the `playlist_items_manual` function. Meaning the following spotify endpoint: `https://api.spotify.com/v1/playlists/0hnM986qSkpI9YSFwpl96E/tracks`
Author
Owner

@ramsayleung commented on GitHub (May 27, 2025):

This is my unit test, it's passed, I can't re-produce this problem:

#[maybe_async::test(
    feature = "__sync",
    async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test),
    async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test)
)]
async fn test_playlist_deserialize() {
    let playlist_id = PlaylistId::from_id("0hnM986qSkpI9YSFwpl96E").unwrap();
    let result = creds_client().await.playlist_items_manual(playlist_id, None, None, None, None).await;
    println!("result: {:#?}", result);
}

Could you run this test, and post your output?

cargo test --no-default-features --features=env-file,client-ureq,ureq-rustls-tls -- --nocapture > /tmp/cargo-out.txt
<!-- gh-comment-id:2911052287 --> @ramsayleung commented on GitHub (May 27, 2025): This is my unit test, it's passed, I can't re-produce this problem: ```rs #[maybe_async::test( feature = "__sync", async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) )] async fn test_playlist_deserialize() { let playlist_id = PlaylistId::from_id("0hnM986qSkpI9YSFwpl96E").unwrap(); let result = creds_client().await.playlist_items_manual(playlist_id, None, None, None, None).await; println!("result: {:#?}", result); } ``` Could you run this test, and post your output? ``` cargo test --no-default-features --features=env-file,client-ureq,ureq-rustls-tls -- --nocapture > /tmp/cargo-out.txt ```
Author
Owner

@DrunkenToast commented on GitHub (Jul 6, 2025):

Running into the same issue, although not with the playlist above, instead with this one: 4MAcblnU4nwtIZ267YI24a

Response: Err(
    ParseJson(
        Error("data did not match any variant of untagged enum PlayableItem", line: 1, column: 52188),
    ),
)

The output of the request is this: out.txt

Seems to be on the video thumbnail? One of the tracks is a podcast episode. Not sure where the issue lies but hope it helps.

<!-- gh-comment-id:3042009964 --> @DrunkenToast commented on GitHub (Jul 6, 2025): Running into the same issue, although not with the playlist above, instead with this one: `4MAcblnU4nwtIZ267YI24a` ``` Response: Err( ParseJson( Error("data did not match any variant of untagged enum PlayableItem", line: 1, column: 52188), ), ) ``` The output of the request is this: [out.txt](https://github.com/user-attachments/files/21089851/out.txt) Seems to be on the video thumbnail? One of the tracks is a podcast episode. Not sure where the issue lies but hope it helps.
Author
Owner

@ramsayleung commented on GitHub (Jul 7, 2025):

The root cause should be the "type": "REALPODCASTNOTMUSIC123", Spotify rollouts a new type again.

It's a recurring problem I encounter again and again, I couldn't count how many times Spotify rollout a new field or new variant without properly updating their CHANGELOG, and user will create an issue to report the JSON deserialization error.

I am proposing a more robust way to minimize the impact to this library.

<!-- gh-comment-id:3043239701 --> @ramsayleung commented on GitHub (Jul 7, 2025): The root cause should be the `"type": "REALPODCASTNOTMUSIC123"`, Spotify rollouts a new type again. It's a recurring problem I encounter again and again, I couldn't count how many times Spotify rollout a new field or new variant without properly updating their CHANGELOG, and user will create an [issue](https://github.com/ramsayleung/rspotify/issues?q=sort%3Aupdated-desc%20is%3Aissue%20JSON) to report the JSON deserialization error. I am proposing a more robust way to minimize the impact to this library.
Author
Owner

@DrunkenToast commented on GitHub (Jul 7, 2025):

That seems like the most sensible solution. Thanks for the hard work. The Spotify API seems like quite a mess :)

<!-- gh-comment-id:3046568655 --> @DrunkenToast commented on GitHub (Jul 7, 2025): That seems like the most sensible solution. Thanks for the hard work. The Spotify API seems like quite a mess :)
Author
Owner

@ramsayleung commented on GitHub (Jul 8, 2025):

I eventually figure out the root cause of this JSON error, I can reproduce the problem with this test:

#[test]
#[wasm_bindgen_test]
fn test_deserialization_playlist_item_with_malformed_episodes() {
    // To fix https://github.com/ramsayleung/rspotify/issues/525
    let json = r#"
{
  "href": "https://api.spotify.com/v1/playlists/4MAcblnU4nwtIZ267YI24a/tracks?offset=0&limit=100",
  "items": [
    {
      "added_at": "2024-12-14T13:23:53Z",
      "added_by": {
        "external_urls": {
          "spotify": "https://open.spotify.com/user/31rhaare4k4nkr5bngqyiiz4bq6a"
        },
        "href": "https://api.spotify.com/v1/users/31rhaare4k4nkr5bngqyiiz4bq6a",
        "id": "31rhaare4k4nkr5bngqyiiz4bq6a",
        "type": "user",
        "uri": "spotify:user:31rhaare4k4nkr5bngqyiiz4bq6a"
      },
      "is_local": false,
      "primary_color": null,
      "track": {
        "preview_url": "https://podz-content.spotifycdn.com/audio/clips/3pEQnFvjtRVnJURokrIEaH/clip_46844_106844.mp3",
        "available_markets": ["AD"],
        "explicit": false,
        "type": "episode",
        "episode": false,
        "track": true,
        "album": {
          "available_markets": ["AD"],
          "type": "show",
          "album_type": "compilation",
          "href": "https://api.spotify.com/v1/shows/4C53FwIBO9tEhJ0dkMN3GN",
          "id": "4C53FwIBO9tEhJ0dkMN3GN",
          "images": [
            {
              "height": 64,
              "url": "https://i.scdn.co/image/ab6765630000f68dc5087ef7f8e3caa4fd6e6bcd",
              "width": 64
            }
          ],
          "name": "REALPODCASTNOTMUSIC123",
          "release_date": null,
          "release_date_precision": null,
          "uri": "spotify:show:4C53FwIBO9tEhJ0dkMN3GN",
          "artists": [
            {
              "external_urls": {
                "spotify": "https://open.spotify.com/show/4C53FwIBO9tEhJ0dkMN3GN"
              },
              "href": "https://api.spotify.com/v1/shows/4C53FwIBO9tEhJ0dkMN3GN",
              "id": "4C53FwIBO9tEhJ0dkMN3GN",
              "name": null,
              "type": "REALPODCASTNOTMUSIC123",
              "uri": "spotify:show:4C53FwIBO9tEhJ0dkMN3GN"
            }
          ],
          "external_urls": {
            "spotify": "https://open.spotify.com/album/4C53FwIBO9tEhJ0dkMN3GN"
          },
          "total_tracks": 1
        },
        "artists": [
          {
            "external_urls": {
              "spotify": "https://open.spotify.com/show/4C53FwIBO9tEhJ0dkMN3GN"
            },
            "href": "https://api.spotify.com/v1/shows/4C53FwIBO9tEhJ0dkMN3GN",
            "id": "4C53FwIBO9tEhJ0dkMN3GN",
            "name": null,
            "type": "REALPODCASTNOTMUSIC123",
            "uri": "spotify:show:4C53FwIBO9tEhJ0dkMN3GN"
          }
        ],
        "disc_number": 0,
        "track_number": 0,
        "duration_ms": 119257,
        "external_ids": {
          "spotify": "https://open.spotify.com/episode/4IBGQd8aV4j6WGqGOjdJmE"
        },
        "external_urls": {
          "spotify": "https://open.spotify.com/episode/4IBGQd8aV4j6WGqGOjdJmE"
        },
        "href": "https://api.spotify.com/v1/episodes/4IBGQd8aV4j6WGqGOjdJmE",
        "id": "4IBGQd8aV4j6WGqGOjdJmE",
        "name": "ITS BEGINNING TO LOOK A LOT LIKE CHRISTMAS (Mac Demarco)",
        "popularity": 0,
        "uri": "spotify:episode:4IBGQd8aV4j6WGqGOjdJmE",
        "is_local": false
      },
      "video_thumbnail": {
        "url": null
      }
    }
  ],
  "limit": 100,
  "next": null,
  "offset": 0,
  "previous": null,
  "total": 17
}
"#;
    let page: Page<PlaylistItem> = deserialize(json);
    assert_eq!(page.total, 1);
}

"track": {
  "type": "episode",     // <- This says it's an episode
  "episode": false,      // <- But this contradicts it
  "track": true,         // <- And this says it's a track
}

The problem is that the PlayableItem enum expects either a Track or Episode, but the JSON contains a hybrid object that has characteristics of both.

But based on API doc of get playlist item:

Information about the track or episode.
Will be one of the following:

It's pretty frustrating.

<!-- gh-comment-id:3047367009 --> @ramsayleung commented on GitHub (Jul 8, 2025): I eventually figure out the root cause of this JSON error, I can reproduce the problem with this test: ```rust #[test] #[wasm_bindgen_test] fn test_deserialization_playlist_item_with_malformed_episodes() { // To fix https://github.com/ramsayleung/rspotify/issues/525 let json = r#" { "href": "https://api.spotify.com/v1/playlists/4MAcblnU4nwtIZ267YI24a/tracks?offset=0&limit=100", "items": [ { "added_at": "2024-12-14T13:23:53Z", "added_by": { "external_urls": { "spotify": "https://open.spotify.com/user/31rhaare4k4nkr5bngqyiiz4bq6a" }, "href": "https://api.spotify.com/v1/users/31rhaare4k4nkr5bngqyiiz4bq6a", "id": "31rhaare4k4nkr5bngqyiiz4bq6a", "type": "user", "uri": "spotify:user:31rhaare4k4nkr5bngqyiiz4bq6a" }, "is_local": false, "primary_color": null, "track": { "preview_url": "https://podz-content.spotifycdn.com/audio/clips/3pEQnFvjtRVnJURokrIEaH/clip_46844_106844.mp3", "available_markets": ["AD"], "explicit": false, "type": "episode", "episode": false, "track": true, "album": { "available_markets": ["AD"], "type": "show", "album_type": "compilation", "href": "https://api.spotify.com/v1/shows/4C53FwIBO9tEhJ0dkMN3GN", "id": "4C53FwIBO9tEhJ0dkMN3GN", "images": [ { "height": 64, "url": "https://i.scdn.co/image/ab6765630000f68dc5087ef7f8e3caa4fd6e6bcd", "width": 64 } ], "name": "REALPODCASTNOTMUSIC123", "release_date": null, "release_date_precision": null, "uri": "spotify:show:4C53FwIBO9tEhJ0dkMN3GN", "artists": [ { "external_urls": { "spotify": "https://open.spotify.com/show/4C53FwIBO9tEhJ0dkMN3GN" }, "href": "https://api.spotify.com/v1/shows/4C53FwIBO9tEhJ0dkMN3GN", "id": "4C53FwIBO9tEhJ0dkMN3GN", "name": null, "type": "REALPODCASTNOTMUSIC123", "uri": "spotify:show:4C53FwIBO9tEhJ0dkMN3GN" } ], "external_urls": { "spotify": "https://open.spotify.com/album/4C53FwIBO9tEhJ0dkMN3GN" }, "total_tracks": 1 }, "artists": [ { "external_urls": { "spotify": "https://open.spotify.com/show/4C53FwIBO9tEhJ0dkMN3GN" }, "href": "https://api.spotify.com/v1/shows/4C53FwIBO9tEhJ0dkMN3GN", "id": "4C53FwIBO9tEhJ0dkMN3GN", "name": null, "type": "REALPODCASTNOTMUSIC123", "uri": "spotify:show:4C53FwIBO9tEhJ0dkMN3GN" } ], "disc_number": 0, "track_number": 0, "duration_ms": 119257, "external_ids": { "spotify": "https://open.spotify.com/episode/4IBGQd8aV4j6WGqGOjdJmE" }, "external_urls": { "spotify": "https://open.spotify.com/episode/4IBGQd8aV4j6WGqGOjdJmE" }, "href": "https://api.spotify.com/v1/episodes/4IBGQd8aV4j6WGqGOjdJmE", "id": "4IBGQd8aV4j6WGqGOjdJmE", "name": "ITS BEGINNING TO LOOK A LOT LIKE CHRISTMAS (Mac Demarco)", "popularity": 0, "uri": "spotify:episode:4IBGQd8aV4j6WGqGOjdJmE", "is_local": false }, "video_thumbnail": { "url": null } } ], "limit": 100, "next": null, "offset": 0, "previous": null, "total": 17 } "#; let page: Page<PlaylistItem> = deserialize(json); assert_eq!(page.total, 1); } ``` ```json "track": { "type": "episode", // <- This says it's an episode "episode": false, // <- But this contradicts it "track": true, // <- And this says it's a track } ``` The problem is that the `PlayableItem` enum expects either a `Track` or `Episode`, but the JSON contains a **hybrid** object that has characteristics of both. But based on API doc of [get playlist item](https://developer.spotify.com/documentation/web-api/reference/get-playlists-tracks): > Information about the track or episode. > Will be one of the following: It's pretty frustrating.
Author
Owner

@ramsayleung commented on GitHub (Jul 8, 2025):

The patch has been merged into the main branch, currently you could consume the main branch to resolve the JSON error until I release a new version.

<!-- gh-comment-id:3047498642 --> @ramsayleung commented on GitHub (Jul 8, 2025): The patch has been merged into the main branch, currently you could consume the main branch to resolve the JSON error until I release a new version.
Author
Owner

@DrunkenToast commented on GitHub (Jul 8, 2025):

Hahaha, hilarious! Beautiful API design and documentation.
Thank you again for resolving this so quickly! I was using reqwest manually as temporary solution but I am happy to switch back to this library. :)

<!-- gh-comment-id:3049152734 --> @DrunkenToast commented on GitHub (Jul 8, 2025): Hahaha, hilarious! Beautiful API design and documentation. Thank you again for resolving this so quickly! I was using reqwest manually as temporary solution but I am happy to switch back to this library. :)
Author
Owner

@hrkfdn commented on GitHub (Aug 26, 2025):

Hey @ramsayleung, thanks a lot for fixing this. I'm also getting user reports that look like this. Any chance of a patch release soon? :)

<!-- gh-comment-id:3225361514 --> @hrkfdn commented on GitHub (Aug 26, 2025): Hey @ramsayleung, thanks a lot for fixing this. I'm also getting user reports that look like this. Any chance of a patch release soon? :)
Author
Owner

@ramsayleung commented on GitHub (Aug 28, 2025):

Request received and processed, the new version of this patch is out :)

https://crates.io/crates/rspotify

<!-- gh-comment-id:3231512816 --> @ramsayleung commented on GitHub (Aug 28, 2025): Request received and processed, the new version of this patch is out :) https://crates.io/crates/rspotify
Author
Owner

@hrkfdn commented on GitHub (Aug 28, 2025):

Thanks so much! :)

<!-- gh-comment-id:3231698757 --> @hrkfdn commented on GitHub (Aug 28, 2025): Thanks so much! :)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/rspotify#167
No description provided.