{"record":{"id":"f25c2ecf9305a26c","repo":"ppy/osu","slug":"local-user-is-not-the-room-host","errorCode":null,"errorMessage":"Local user is not the room host.","messagePattern":"Local user is not the room host\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs","lineNumber":544,"sourceCode":"\r\n            ChangeUserState(LocalUser.UserID, MultiplayerUserState.Idle);\r\n\r\n            return Task.CompletedTask;\r\n        }\r\n\r\n        public override async Task AbortMatch()\r\n        {\r\n            ChangeUserState(api.LocalUser.Value.Id, MultiplayerUserState.Idle);\r\n            await ((IMultiplayerClient)this).GameplayAborted(GameplayAbortReason.HostAbortedTheMatch).ConfigureAwait(false);\r\n        }\r\n\r\n        public async Task AddUserPlaylistItem(int userId, MultiplayerPlaylistItem item)\r\n        {\r\n            Debug.Assert(ServerRoom != null);\r\n            Debug.Assert(currentItem != null);\r\n\r\n            if (ServerRoom.Settings.QueueMode == QueueMode.HostOnly && ServerRoom.Host?.UserID != LocalUser?.UserID)\r\n                throw new InvalidOperationException(\"Local user is not the room host.\");\r\n\r\n            item.OwnerID = userId;\r\n\r\n            await addItem(item).ConfigureAwait(false);\r\n            await updateCurrentItem(ServerRoom).ConfigureAwait(false);\r\n            updateRoomStateIfRequired();\r\n        }\r\n\r\n        public override Task AddPlaylistItem(MultiplayerPlaylistItem item) => AddUserPlaylistItem(api.LocalUser.Value.OnlineID, clone(item));\r\n\r\n        public async Task EditUserPlaylistItem(int userId, MultiplayerPlaylistItem item)\r\n        {\r\n            Debug.Assert(ServerRoom != null);\r\n            Debug.Assert(currentItem != null);\r\n            Debug.Assert(ServerAPIRoom != null);\r\n\r\n            item.OwnerID = userId;\r\n\r","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/ppy/osu/blob/d9c73e12adff2feaae4a3e158d36fe5883faf6ca/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs#L526-L562","documentation":"AddUserPlaylistItem enforces server-side authorization: when the room's QueueMode is HostOnly, only the room host may add playlist items, otherwise it throws. This mirrors the real multiplayer server's host-only queue rule.","triggerScenarios":"Calling AddPlaylistItem/AddUserPlaylistItem while QueueMode == HostOnly and the local user is not ServerRoom.Host (LocalUser?.UserID != ServerRoom.Host?.UserID).","commonSituations":"Test sets QueueMode.HostOnly but performs the add as a non-host user; the acting user id was never made host; multi-user test where the wrong client adds.","solutions":["Make the acting user the host before adding (e.g. TransferHost(localUserId)).","Switch QueueMode to AllPlayers or PlayerOnly so any user can add.","Call AddUserPlaylistItem with the host user's id instead of the non-host's."],"exampleFix":"// before\nclient.ChangeSettings(queueMode: QueueMode.HostOnly);\nawait client.AddPlaylistItem(item); // non-host throws\n\n// after\nclient.TransferHost(localUserId);\nawait client.AddPlaylistItem(item);","handlingStrategy":"validation","validationCode":"if (ServerRoom.Settings.QueueMode == QueueMode.HostOnly\n    && ServerRoom.Host?.UserID != LocalUser?.UserID)\n{\n    client.TransferHost(api.LocalUser.Value.OnlineID);\n}\nawait client.AddPlaylistItem(item);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Before adding items under HostOnly, ensure the actor is host or switch QueueMode.","In multi-user tests, add via the host client only.","Centralize playlist-add helpers that check QueueMode + host first."],"tags":["test-infra","multiplayer","authorization","queue-mode"],"backgroundTag":null,"analyzedSha":"d9c73e12adff2feaae4a3e158d36fe5883faf6ca","analyzedAt":"2026-08-13T14:12:54.015Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}