ppy/osu · error · InvalidOperationException

The room's current item cannot be removed.

Error message

The room's current item cannot be removed.

What it means

RemoveUserPlaylistItem protects the currently-active playlist item (currentItem, i.e. ServerRoom.Playlist[currentIndex]) from removal to keep room state consistent while a beatmap is in play. Removing it throws.

Source

Thrown at osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs:596

            ServerAPIRoom.Playlist = ServerAPIRoom.Playlist.Select((pi, i) => pi.ID == item.ID ? new PlaylistItem(item) : ServerAPIRoom.Playlist[i]).ToArray();

            await ((IMultiplayerClient)this).PlaylistItemChanged(clone(item)).ConfigureAwait(false);
        }

        public override Task EditPlaylistItem(MultiplayerPlaylistItem item) => EditUserPlaylistItem(api.LocalUser.Value.OnlineID, clone(item));

        public async Task RemoveUserPlaylistItem(int userId, long playlistItemId)
        {
            Debug.Assert(ServerRoom != null);
            Debug.Assert(ServerAPIRoom != null);

            var item = ServerRoom.Playlist.FirstOrDefault(i => i.ID == playlistItemId);

            if (item == null)
                throw new InvalidOperationException("Item does not exist in the room.");

            if (item.Equals(currentItem))
                throw new InvalidOperationException("The room's current item cannot be removed.");

            if (item.OwnerID != userId)
                throw new InvalidOperationException("Attempted to remove an item which is not owned by the user.");

            if (item.Expired)
                throw new InvalidOperationException("Attempted to remove an item which has already been played.");

            ServerRoom.Playlist.Remove(item);
            ServerAPIRoom.Playlist = ServerAPIRoom.Playlist.Where(i => i.ID != item.ID).ToArray();
            await ((IMultiplayerClient)this).PlaylistItemRemoved(clone(playlistItemId)).ConfigureAwait(false);

            await updateCurrentItem(ServerRoom).ConfigureAwait(false);
            updateRoomStateIfRequired();
        }

        public override Task RemovePlaylistItem(long playlistItemId) => RemoveUserPlaylistItem(api.LocalUser.Value.OnlineID, clone(playlistItemId));

        public override Task VoteToSkipIntro()

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Remove a different (non-current) playlist item.
  2. Advance the room past the item (finish/advance the match so currentItem changes) before removing it.
  3. If clearing the playlist, expire/skip the current item first via the normal flow.

Example fix

// before
await client.RemovePlaylistItem(currentItem.ID.Value); // throws

// after
// remove a queued (non-current) item instead:
await client.RemovePlaylistItem(ServerRoom.Playlist.First(i => i.ID != currentItem.ID && !i.Expired).ID);
Defensive patterns

Strategy: validation

Validate before calling

if (playlistItemId == currentItem?.ID)
    throw new InvalidOperationException("cannot remove the active item");
await client.RemovePlaylistItem(playlistItemId);

Prevention

When it happens

Trigger: Calling RemovePlaylistItem with the ID of the item currently being played (item.Equals(currentItem) is true).

Common situations: Test tries to remove the item that the room is actively on; currentItem pointer was not advanced before removal.

Related errors


AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13). Data as JSON: /api/errors/1c7538d1cb8dc90b. Report an issue: GitHub.