ppy/osu · error · InvalidOperationException
Attempted to change an item which has already been played.
Error message
Attempted to change an item which has already been played.
What it means
EditUserPlaylistItem refuses to modify items whose Expired flag is true. The server marks a playlist item Expired once it has been played, and expired items are immutable.
Source
Thrown at osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs:572
public async Task EditUserPlaylistItem(int userId, MultiplayerPlaylistItem item)
{
Debug.Assert(ServerRoom != null);
Debug.Assert(currentItem != null);
Debug.Assert(ServerAPIRoom != null);
item.OwnerID = userId;
var existingItem = ServerRoom.Playlist.SingleOrDefault(i => i.ID == item.ID);
if (existingItem == null)
throw new InvalidOperationException("Attempted to change an item that doesn't exist.");
if (existingItem.OwnerID != userId && ServerRoom.Host?.UserID != LocalUser?.UserID)
throw new InvalidOperationException("Attempted to change an item which is not owned by the user.");
if (existingItem.Expired)
throw new InvalidOperationException("Attempted to change an item which has already been played.");
// Ensure the playlist order doesn't change.
item.PlaylistOrder = existingItem.PlaylistOrder;
ServerRoom.Playlist[ServerRoom.Playlist.IndexOf(existingItem)] = item;
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);
View on GitHub (pinned to d9c73e12ad)
Solutions
- Add a fresh playlist item and edit that instead of an expired one.
- Edit the item before it gets played (before updateCurrentItem expires it).
- If you need to change a played item, remove-and-readd is not possible for expired items either — add a new item with the desired values.
Example fix
// before (item already played) await client.EditPlaylistItem(expiredItem); // after await client.AddPlaylistItem(new MultiplayerPlaylistItem(newBeatmap));
Defensive patterns
Strategy: validation
Validate before calling
var existing = ServerRoom.Playlist.Single(i => i.ID == item.ID);
if (existing.Expired)
throw new InvalidOperationException("item already played; add a new one");
await client.EditPlaylistItem(item); Prevention
- Edit items before they are played (before they expire).
- Filter ServerRoom.Playlist to non-expired items before editing.
- Re-add a fresh item rather than trying to revive an expired one.
When it happens
Trigger: Editing an item that has already been played through (existingItem.Expired == true).
Common situations: Test advanced the playlist past an item (finish/start match) then tried to edit it; editing the currentItem after it expired; reusing a stale item reference.
Related errors
- The room's current item cannot be removed.
- Attempted to remove an item which has already been played.
- Attempted to change an item that doesn't exist.
- Item does not exist in the room.
- Accessing the client-side API room via {nameof(TestMultiplay
AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13).
Data as JSON: /api/errors/d65f6e60fc271c4c.
Report an issue: GitHub.