{"record":{"id":"544fe0b1d99aa97b","repo":"ppy/osu","slug":"invalid-password","errorCode":null,"errorMessage":"Invalid password.","messagePattern":"Invalid password\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs","lineNumber":244,"sourceCode":"\r\n            var user = ServerRoom.Users.Single(u => u.UserID == userId);\r\n            user.BeatmapAvailability = newBeatmapAvailability;\r\n\r\n            ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(clone(userId), clone(user.BeatmapAvailability));\r\n        }\r\n\r\n        protected override async Task<MultiplayerRoom> JoinRoomInternal(long roomId, string? password = null)\r\n        {\r\n            if (RoomJoined || ServerAPIRoom != null)\r\n                throw new InvalidOperationException(\"Already joined a room\");\r\n\r\n            roomId = clone(roomId);\r\n            password = clone(password);\r\n\r\n            ServerAPIRoom = ServerSideRooms.Single(r => r.RoomID == roomId);\r\n\r\n            if (password != ServerAPIRoom.Password)\r\n                throw new InvalidOperationException(\"Invalid password.\");\r\n\r\n            lastPlaylistItemId = ServerAPIRoom.Playlist.Max(item => item.ID);\r\n\r\n            var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id)\r\n            {\r\n                User = api.LocalUser.Value\r\n            };\r\n\r\n            ServerRoom = new MultiplayerRoom(roomId)\r\n            {\r\n                Settings =\r\n                {\r\n                    Name = ServerAPIRoom.Name,\r\n                    MatchType = ServerAPIRoom.Type,\r\n                    Password = password ?? string.Empty,\r\n                    QueueMode = ServerAPIRoom.QueueMode,\r\n                    AutoStartDuration = ServerAPIRoom.AutoStartDuration,\r\n                    MaxParticipants = ServerAPIRoom.MaxParticipants,\r","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/ppy/osu/blob/d9c73e12adff2feaae4a3e158d36fe5883faf6ca/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs#L226-L262","documentation":"JoinRoomInternal resolves the target ServerSideRoom by id, then compares the supplied password to ServerAPIRoom.Password and throws on mismatch. This replicates the real server's password gate inside the in-process test double.","triggerScenarios":"Joining a password-protected room with a null or incorrect password; the room was created with a password but the join call omits it.","commonSituations":"Test creates a room with Password set but joins without it; a cloned room object lost its password; password casing/whitespace mismatch.","solutions":["Pass the room's actual password: await client.JoinRoom(room.RoomID.Value, room.Password).","If the test does not need password protection, create the room with a null/empty password.","Verify the password string equals ServerSideRooms.Single(r => r.RoomID == id).Password before joining."],"exampleFix":"// before\nawait client.JoinRoom(room.RoomID.Value);\n\n// after\nawait client.JoinRoom(room.RoomID.Value, room.Password);","handlingStrategy":"validation","validationCode":"string? pwd = room.Password;\nif (pwd != ServerSideRooms.Single(r => r.RoomID == room.RoomID).Password)\n    throw new InvalidOperationException(\"password mismatch pre-check\");\nawait client.JoinRoom(room.RoomID.Value, pwd);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass room.Password into JoinRoom rather than a literal.","If the test does not need a password, create the room without one.","Keep the password on the same Room object you join with to avoid drift."],"tags":["test-infra","multiplayer","auth","password"],"backgroundTag":null,"analyzedSha":"d9c73e12adff2feaae4a3e158d36fe5883faf6ca","analyzedAt":"2026-08-13T14:12:54.015Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}