ppy/osu · error · InvalidOperationException
Attempted to change an item which is not owned by the user.
Error message
Attempted to change an item which is not owned by the user.
What it means
EditUserPlaylistItem requires the editor be the item's owner OR the room host; editing another user's item without host privileges throws. This mirrors the server's ownership authorization on playlist edits.
Source
Thrown at osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs:569
}
public override Task AddPlaylistItem(MultiplayerPlaylistItem item) => AddUserPlaylistItem(api.LocalUser.Value.OnlineID, clone(item));
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);
View on GitHub (pinned to d9c73e12ad)
Solutions
- Edit the item as the user who owns it (item.OwnerID).
- Act as the room host (TransferHost) before editing another user's item.
- Re-create the item under the acting user if ownership transfer is not intended.
Example fix
// before await client.EditUserPlaylistItem(nonOwnerUserId, item); // after await client.EditUserPlaylistItem(item.OwnerID, item); // or make the actor host first: client.TransferHost(actorUserId); await client.EditUserPlaylistItem(actorUserId, item);
Defensive patterns
Strategy: validation
Validate before calling
var existing = ServerRoom.Playlist.Single(i => i.ID == item.ID);
if (existing.OwnerID != userId && ServerRoom.Host?.UserID != userId)
throw new InvalidOperationException("not owner and not host");
await client.EditUserPlaylistItem(userId, item); Prevention
- Edit items as their OwnerID user, or as the host.
- Track which user added each item in multi-user tests.
- Use TransferHost when a non-owner must modify another's item.
When it happens
Trigger: Editing an item whose OwnerID differs from the acting userId while ServerRoom.Host?.UserID is also not the acting user.
Common situations: Test edits as a different user than the one who added the item; the host carve-out was assumed but the acting user is not host.
Related errors
- Attempted to remove 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/32bc45e882bebb93.
Report an issue: GitHub.