ppy/osu · error · InvalidOperationException
Attempted to remove an item which is not owned by the user.
Error message
Attempted to remove an item which is not owned by the user.
What it means
RemoveUserPlaylistItem enforces ownership: only the item's owner may remove it (there is no host carve-out in the remove path, unlike edit). Removing an item whose OwnerID differs from the acting userId throws.
Source
Thrown at osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs:599
}
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()
{
return UserVoteToSkipIntro(api.LocalUser.Value.OnlineID);
}
View on GitHub (pinned to d9c73e12ad)
Solutions
- Remove the item as its owner (call with userId == item.OwnerID).
- If a different user must trigger removal, have that user own the item in the first place.
Example fix
// before await client.RemoveUserPlaylistItem(nonOwnerUserId, itemId); // after await client.RemoveUserPlaylistItem(item.OwnerID, itemId);
Defensive patterns
Strategy: validation
Validate before calling
var item = ServerRoom.Playlist.FirstOrDefault(i => i.ID == playlistItemId);
if (item != null && item.OwnerID != userId)
throw new InvalidOperationException("only the owner may remove");
await client.RemoveUserPlaylistItem(userId, playlistItemId); Prevention
- Remember the remove path has no host carve-out — only the owner may remove.
- Remove items as the user who added them.
- Do not assume host privileges extend to removal.
When it happens
Trigger: Calling RemoveUserPlaylistItem/RemovePlaylistItem as a user whose id != item.OwnerID.
Common situations: Test removes an item as a non-owner; assuming host can remove others' items (the remove path does not grant that).
Related errors
- Attempted to change an item which is not owned by the user.
- Local user is not the room host.
- Accessing the client-side API room via {nameof(TestMultiplay
- Accessing the client-side room via {nameof(TestMultiplayerCl
- Already joined a room
AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13).
Data as JSON: /api/errors/d21482507f588b34.
Report an issue: GitHub.