[GH-ISSUE #271] SpotifyWebAPIException: Missing or bad version tag #197

Open
opened 2026-02-27 19:26:33 +03:00 by kerem · 13 comments
Owner

Originally created by @kasperkamperman on GitHub (Feb 29, 2024).
Original GitHub issue: https://github.com/jwilsson/spotify-web-api-php/issues/271

I suddenly get an error when I'm deleting tracks. I didn't change any code or updated anything.
Which makes me assume something changes on the side of Spotify (couldn't find announcements though).

Fatal error:  Uncaught SpotifyWebAPI\SpotifyWebAPIException: Missing or bad version tag in <redacted>/vendor/jwilsson/spotify-web-api-php/src/Request.php:47
Stack trace:
#0 <redacted>/vendor/jwilsson/spotify-web-api-php/src/Request.php(243): SpotifyWebAPI\Request->handleResponseError()
#1 <redacted>/vendor/jwilsson/spotify-web-api-php/src/Request.php(131): SpotifyWebAPI\Request->send()
#2 <redacted>/vendor/jwilsson/spotify-web-api-php/src/SpotifyWebAPI.php(122): SpotifyWebAPI\Request->api()
#3 <redacted>/vendor/jwilsson/spotify-web-api-php/src/SpotifyWebAPI.php(571): SpotifyWebAPI\SpotifyWebAPI->sendRequest()
#4 <redacted>/cronfresh.php(357): SpotifyWebAPI\SpotifyWebAPI->deletePlaylistTracks()
#5 <redacted>/cronfresh.php(231): addTrackToDestination()
#6 {main}
  thrown in <redacted>/vendor/jwilsson/spotify-web-api-php/src/Request.php on line 47

I delete tracks based on the positions:

$positionsToRemove = [
    [positions] => [
            [0] => 0
            [1] => 1
            [2] => 2
            [3] => 3
            [4] => 4
        ]
]

$api->deletePlaylistTracks($destinationId, $positionsToRemove, $snapShotId);

I was checking the Spotify API documentation and this way of deleting is not documented (anymore):
https://developer.spotify.com/documentation/web-api/reference/remove-tracks-playlist

I looked quickly and it seems you didn't build anything to convert positions to spotify uri's. So, that might indeed indicate that there was a silent Spotify API change that remove this function?

Originally created by @kasperkamperman on GitHub (Feb 29, 2024). Original GitHub issue: https://github.com/jwilsson/spotify-web-api-php/issues/271 I suddenly get an error when I'm deleting tracks. I didn't change any code or updated anything. Which makes me assume something changes on the side of Spotify (couldn't find announcements though). ``` Fatal error: Uncaught SpotifyWebAPI\SpotifyWebAPIException: Missing or bad version tag in <redacted>/vendor/jwilsson/spotify-web-api-php/src/Request.php:47 Stack trace: #0 <redacted>/vendor/jwilsson/spotify-web-api-php/src/Request.php(243): SpotifyWebAPI\Request->handleResponseError() #1 <redacted>/vendor/jwilsson/spotify-web-api-php/src/Request.php(131): SpotifyWebAPI\Request->send() #2 <redacted>/vendor/jwilsson/spotify-web-api-php/src/SpotifyWebAPI.php(122): SpotifyWebAPI\Request->api() #3 <redacted>/vendor/jwilsson/spotify-web-api-php/src/SpotifyWebAPI.php(571): SpotifyWebAPI\SpotifyWebAPI->sendRequest() #4 <redacted>/cronfresh.php(357): SpotifyWebAPI\SpotifyWebAPI->deletePlaylistTracks() #5 <redacted>/cronfresh.php(231): addTrackToDestination() #6 {main} thrown in <redacted>/vendor/jwilsson/spotify-web-api-php/src/Request.php on line 47 ``` I delete tracks based on the positions: ``` $positionsToRemove = [ [positions] => [ [0] => 0 [1] => 1 [2] => 2 [3] => 3 [4] => 4 ] ] $api->deletePlaylistTracks($destinationId, $positionsToRemove, $snapShotId); ``` I was checking the Spotify API documentation and this way of deleting is not documented (anymore): https://developer.spotify.com/documentation/web-api/reference/remove-tracks-playlist I looked quickly and it seems you didn't build anything to convert positions to spotify uri's. So, that might indeed indicate that there was a silent Spotify API change that remove this function?
Author
Owner

@jwilsson commented on GitHub (Feb 29, 2024):

Hey!
That's strange. It does sound like passing positions was removed from the Spotify docs at some point. But this code (copied from the tests added 6 years ago works 😅):

$api->deletePlaylistTracks(
    '2uavw0Rfozgta0baWuK3dm',
    [
        'positions' => [
            0,
            1,
        ],
    ],
    'NywxOTU1MGZiMmZhN2VhMzViMjMwMzIxMjQyYmQyODBjYzFlYWYxOGY1'
);
<!-- gh-comment-id:1971540718 --> @jwilsson commented on GitHub (Feb 29, 2024): Hey! That's strange. It does sound like passing `positions` was removed from the Spotify docs at some point. But this code (copied from the tests added 6 years ago works 😅): ```php $api->deletePlaylistTracks( '2uavw0Rfozgta0baWuK3dm', [ 'positions' => [ 0, 1, ], ], 'NywxOTU1MGZiMmZhN2VhMzViMjMwMzIxMjQyYmQyODBjYzFlYWYxOGY1' ); ```
Author
Owner

@kasperkamperman commented on GitHub (Feb 29, 2024):

I noticed it works again somehow. Will try if it keeps like this and share my findings.

<!-- gh-comment-id:1971614092 --> @kasperkamperman commented on GitHub (Feb 29, 2024): I noticed it works again somehow. Will try if it keeps like this and share my findings.
Author
Owner

@kasperkamperman commented on GitHub (Mar 13, 2024):

It worked for a week, now again Spotify refuses to delete songs.

I get the error: Missing or bad version tag

I tried removing the snapShotID but that results in:
Must specify snapshot_id when setting positions/n

My assumption is that the positions part is removed or not stable. It's a pity. Because now I have to give track id's, however that would remove probably duplicates too (which I don't want).

<!-- gh-comment-id:1993962709 --> @kasperkamperman commented on GitHub (Mar 13, 2024): It worked for a week, now again Spotify refuses to delete songs. I get the error: ```Missing or bad version tag``` I tried removing the snapShotID but that results in: ```Must specify snapshot_id when setting positions/n``` My assumption is that the positions part is removed or not stable. It's a pity. Because now I have to give track id's, however that would remove probably duplicates too (which I don't want).
Author
Owner

@kasperkamperman commented on GitHub (Mar 13, 2024):

It seems that I have the same issues calling a supported (at last api documentation) way. Like passing track id's.
Same error: Missing or bad version tag.

It might be an issue with the snapshotID then, which I get from a previous call to addPlaylistTracks.

$removeTracks = [];

    foreach ($destinationListData['items'] as $index => $item) {
        $epoch = strtotime($item['added_at']);
        //echo "epoch: ".$epoch."\n";
        if($epoch > $limitEpoch) {
            // jump out directly since all the other tracks are newer
            // so no need to check
            break;
        }

        $removeTracks[] = ['uri' => $item['track']['uri'], 'positions' => [$index]];
    }

    if($debug) {
        echo "removeTracks: ";
        print_r($removeTracks);
    }

    if(count($removeTracks)>0) {
        $tracksToRemove = [
            'tracks' => $removeTracks
        ];

        try {
            $api->deletePlaylistTracks($destinationId, $tracksToRemove, $snapShotId);
        }
        catch (Exception $e) {
            //$lastResponse = $api->getRequest()->getLastResponse();
            //var_dump($lastResponse);
            echo $e->getMessage()."/n";  
        }
              
    }
<!-- gh-comment-id:1995420786 --> @kasperkamperman commented on GitHub (Mar 13, 2024): It seems that I have the same issues calling a supported (at last api documentation) way. Like passing track id's. Same error: Missing or bad version tag. It might be an issue with the snapshotID then, which I get from a previous call to addPlaylistTracks. ``` $removeTracks = []; foreach ($destinationListData['items'] as $index => $item) { $epoch = strtotime($item['added_at']); //echo "epoch: ".$epoch."\n"; if($epoch > $limitEpoch) { // jump out directly since all the other tracks are newer // so no need to check break; } $removeTracks[] = ['uri' => $item['track']['uri'], 'positions' => [$index]]; } if($debug) { echo "removeTracks: "; print_r($removeTracks); } if(count($removeTracks)>0) { $tracksToRemove = [ 'tracks' => $removeTracks ]; try { $api->deletePlaylistTracks($destinationId, $tracksToRemove, $snapShotId); } catch (Exception $e) { //$lastResponse = $api->getRequest()->getLastResponse(); //var_dump($lastResponse); echo $e->getMessage()."/n"; } } ```
Author
Owner

@kasperkamperman commented on GitHub (Mar 13, 2024):

I'm testing in the API console and there it still seems to work.

{
    "positions":  [0,1],
    "snapshot_id": "MTIsMzQ5NWMwNmZlOGE1MGYyOTg4ZGZhNzlhYWU2NjFiYjk2MGJlMjNiMg=="
}

What I noticed. I get a snapshotId when I call addPlaylistTracks.
This snapshotId is the one I use to pass to deletePlaylistTracks($destinationId, $positionsToRemove, $snapShotId).

I could get a recent snapshotId in the api console by calling:

{
    "tracks": []
}

This works in this PHP api too:

$id = $api->deletePlaylistTracks($destinationId, $tracksToRemove);
echo "snapshot id:".$id;

What I noticed it that the snapshotId I got back from addPlaylistTracks is really shorter then the one I got from deletePlaylistTracks. This happens in this PHP code as well as in the Spotify console.

AAn2GSGCVTwzpvrMt9xvB/Dma6/IZkkW
NjUyODI5LDEyYTg1NzRhOWFiNTJlODZkYmI2YzAxNTczM2JjODg3MzI1ZGI4NzQ=

So my current working workaround is:

$new_id = $api->deletePlaylistTracks($destinationId, ['tracks' => []]);
echo "snapshot id:".$new_id;
$api->deletePlaylistTracks($destinationId, ['positions' => [0]], $new_id);
<!-- gh-comment-id:1995790688 --> @kasperkamperman commented on GitHub (Mar 13, 2024): I'm testing in the API console and there it still seems to work. ``` { "positions": [0,1], "snapshot_id": "MTIsMzQ5NWMwNmZlOGE1MGYyOTg4ZGZhNzlhYWU2NjFiYjk2MGJlMjNiMg==" } ``` What I noticed. I get a snapshotId when I call ```addPlaylistTracks```. This snapshotId is the one I use to pass to ```deletePlaylistTracks($destinationId, $positionsToRemove, $snapShotId)```. I could get a recent snapshotId in the api console by calling: ``` { "tracks": [] } ``` This works in this PHP api too: ``` $id = $api->deletePlaylistTracks($destinationId, $tracksToRemove); echo "snapshot id:".$id; ``` What I noticed it that the snapshotId I got back from addPlaylistTracks is really shorter then the one I got from deletePlaylistTracks. This happens in this PHP code as well as in the Spotify console. ``` AAn2GSGCVTwzpvrMt9xvB/Dma6/IZkkW NjUyODI5LDEyYTg1NzRhOWFiNTJlODZkYmI2YzAxNTczM2JjODg3MzI1ZGI4NzQ= ``` So my current working workaround is: ``` $new_id = $api->deletePlaylistTracks($destinationId, ['tracks' => []]); echo "snapshot id:".$new_id; $api->deletePlaylistTracks($destinationId, ['positions' => [0]], $new_id); ```
Author
Owner

@jwilsson commented on GitHub (Mar 14, 2024):

I saw you're active in the thread on Spotify's forums. That was going to be my suggestion, to reach out there. But like you're saying there, it's really worrisome that the APIs change without any communication.

<!-- gh-comment-id:1997865154 --> @jwilsson commented on GitHub (Mar 14, 2024): I saw you're active in [the thread](https://community.spotify.com/t5/Spotify-for-Developers/Can-t-remove-specific-tracks-from-playlist/td-p/5753116) on Spotify's forums. That was going to be my suggestion, to reach out there. But like you're saying there, it's really worrisome that the APIs change without any communication.
Author
Owner

@vorce commented on GitHub (Mar 21, 2024):

Also having this problem with my little app (which has been working fine for years until lately), it uses the web api in this way:

snapshot_id = add_playlist_track(...)
delete_playlist_track(someuri, snapshot_id) // Will trigger 400, "Missing or bad version tag"

Haven't tried your workaround yet @kasperkamperman, but will do that.

<!-- gh-comment-id:2012070315 --> @vorce commented on GitHub (Mar 21, 2024): Also having this problem with my little app (which has been working fine for years until lately), it uses the web api in this way: ``` snapshot_id = add_playlist_track(...) delete_playlist_track(someuri, snapshot_id) // Will trigger 400, "Missing or bad version tag" ``` Haven't tried your workaround yet @kasperkamperman, but will do that.
Author
Owner

@JMPerez commented on GitHub (May 2, 2024):

Adding another issue that I'm finding here in case you are trying to remove tracks at a specific position. I confirm that Spotify ends up removing all appearances of the track in the playlist and the positions are ignored. This is bad when trying to remove duplicates, as there is no way to leave one instance of the track.

<!-- gh-comment-id:2091524820 --> @JMPerez commented on GitHub (May 2, 2024): Adding another issue that I'm finding here in case you are trying to remove tracks at a specific position. I confirm that Spotify ends up removing all appearances of the track in the playlist and the positions are ignored. This is bad when trying to remove duplicates, as there is no way to leave one instance of the track.
Author
Owner

@kasperkamperman commented on GitHub (Nov 2, 2024):

@JMPerez I was reading your blog on this issue. You mentioned

Update May 8th: Yet another fix deployed to bring back the previous behaviour.

João Vitor, author of Skiley, reached out and told me that it was possible to remove tracks by position alone ﹣ not combined with the song id ﹣ even though it's not documented. I have tested it and it works like a charm, meaning that the we won't need to reset the added time for the duplicated song.

I actually was using this way of removing tracks. Just by using positions, but I found it was broken when I wrote this issue.
Are you actually using this way now in your Dedup tool and is it working stable?

My way of removing positions:

// create an array like [0,2,3,4]
 $positionsToRemove = [
      'positions' => range(0, $tooRemoveCount-1)
];

// because the previous snapShotId was not valid (see issue posts above)
$snapShotId = $api->deletePlaylistTracks($destinationId, ['tracks' => []]);
// delete the tracks (in my case at the beginning)
$api->deletePlaylistTracks($destinationId, $positionsToRemove, $snapShotId);
<!-- gh-comment-id:2452949592 --> @kasperkamperman commented on GitHub (Nov 2, 2024): @JMPerez I was reading your blog on this issue. You mentioned > Update May 8th: Yet another fix deployed to bring back the previous behaviour. > > João Vitor, author of [Skiley](https://skiley.net/), reached out and told me that it was possible to remove tracks by position alone ﹣ not combined with the song id ﹣ even though it's not documented. I have tested it and it works like a charm, meaning that the we won't need to reset the added time for the duplicated song. I actually was using this way of removing tracks. Just by using positions, but I found it was broken when I wrote this issue. Are you actually using this way now in your Dedup tool and is it working stable? My way of removing positions: ``` // create an array like [0,2,3,4] $positionsToRemove = [ 'positions' => range(0, $tooRemoveCount-1) ]; // because the previous snapShotId was not valid (see issue posts above) $snapShotId = $api->deletePlaylistTracks($destinationId, ['tracks' => []]); // delete the tracks (in my case at the beginning) $api->deletePlaylistTracks($destinationId, $positionsToRemove, $snapShotId); ```
Author
Owner

@kasperkamperman commented on GitHub (Nov 23, 2024):

I tested it again, but it doesn't seem to work anymore. I get the error message:

No uris provided

<!-- gh-comment-id:2495494693 --> @kasperkamperman commented on GitHub (Nov 23, 2024): I tested it again, but it doesn't seem to work anymore. I get the error message: > No uris provided
Author
Owner

@jwilsson commented on GitHub (Nov 28, 2024):

Okay, this is really strange. I just tried deleting both by URI and position and both worked 🤔

$api->deletePlaylistTracks('3CuRKFFC2wwSMBwhPl74OT', [
    'tracks' => [
        [
            'uri' => 'spotify:track:2tzs8aV8k9xnbAiaHDn4YP',
        ],
    ],
]);
$response = $api->deletePlaylistTracks('3CuRKFFC2wwSMBwhPl74OT', [
    'positions' => [0],
]);

It's a playlist I just created, don't know if that matters. The only scope I have granted is "playlist-modify-private".

<!-- gh-comment-id:2506690220 --> @jwilsson commented on GitHub (Nov 28, 2024): Okay, this is really strange. I just tried deleting both by URI and position and both worked 🤔 ```php $api->deletePlaylistTracks('3CuRKFFC2wwSMBwhPl74OT', [ 'tracks' => [ [ 'uri' => 'spotify:track:2tzs8aV8k9xnbAiaHDn4YP', ], ], ]); ``` ```php $response = $api->deletePlaylistTracks('3CuRKFFC2wwSMBwhPl74OT', [ 'positions' => [0], ]); ```` It's a playlist I just created, don't know if that matters. The only scope I have granted is "playlist-modify-private".
Author
Owner

@kasperkamperman commented on GitHub (Dec 1, 2024):

Thanks @jwilsson for testing. I did another test myself.

I seems that my workaround (see earlier comments in this topic) for the invalid snapshot id threw this error:
$new_id = $api->deletePlaylistTracks($destinationId, ['tracks' => []]);

I can confirm that removal of positions works again now.
Fingers crossed, because it's undocumented in the API.

<!-- gh-comment-id:2509665262 --> @kasperkamperman commented on GitHub (Dec 1, 2024): Thanks @jwilsson for testing. I did another test myself. I seems that my workaround ([see earlier comments](https://github.com/jwilsson/spotify-web-api-php/issues/271#issuecomment-1993962709) in this topic) for the invalid snapshot id threw this error: `$new_id = $api->deletePlaylistTracks($destinationId, ['tracks' => []]);` I can confirm that removal of positions works again now. Fingers crossed, because it's undocumented in the API.
Author
Owner

@jwilsson commented on GitHub (Feb 19, 2026):

Just want to give a heads up, in the latest version 7.1.0, SpotifyWebAPI::deletePlaylistTracks() has been deprecated due to the February 2026 changes and replaced with SpotifyWebAPI::deletePlaylistItems(). It looks like positions is supported by the new endpoint but it's not documented.

I've not yet decided on when (or if) the deprecated method will be removed or if I'll support positions in the new method given it's undocumented but I wanted to bring your attention to it.

<!-- gh-comment-id:3929279468 --> @jwilsson commented on GitHub (Feb 19, 2026): Just want to give a heads up, in the latest version `7.1.0`, `SpotifyWebAPI::deletePlaylistTracks()` has been deprecated due to the [February 2026 changes](https://developer.spotify.com/blog/2026-02-06-update-on-developer-access-and-platform-security) and replaced with `SpotifyWebAPI::deletePlaylistItems()`. It looks like `positions` is supported by the new endpoint but it's not documented. I've not yet decided on when (or if) the deprecated method will be removed or if I'll support `positions` in the new method given it's undocumented but I wanted to bring your attention to it.
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/spotify-web-api-php#197
No description provided.