{"record":{"id":"f3504286406357ea","repo":"ppy/osu","slug":"cannot-join-an-inactive-room","errorCode":null,"errorMessage":"Cannot join an inactive room.","messagePattern":"Cannot join an inactive room\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"osu.Game/Online/Multiplayer/MultiplayerClient.cs","lineNumber":264,"sourceCode":"                await runOnUpdateThreadAsync(() => pendingRequests.Clear(), cancellationSource.Token).ConfigureAwait(false);\r\n                var multiplayerRoom = await CreateRoomInternal(new MultiplayerRoom(room)).ConfigureAwait(false);\r\n                await setupJoinedRoom(room, multiplayerRoom, cancellationSource.Token).ConfigureAwait(false);\r\n            }, cancellationSource.Token).ConfigureAwait(false);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Joins the <see cref=\"MultiplayerRoom\"/> for a given API <see cref=\"Room\"/>.\r\n        /// </summary>\r\n        /// <param name=\"room\">The API <see cref=\"Room\"/>.</param>\r\n        /// <param name=\"password\">An optional password to use for the join operation.</param>\r\n        /// <exception cref=\"InvalidOperationException\">If the current user is already in another room, or <paramref name=\"room\"/> does not represent an active room.</exception>\r\n        public async Task JoinRoom(Room room, string? password = null)\r\n        {\r\n            if (Room != null)\r\n                throw new InvalidOperationException(\"Cannot join a multiplayer room while already in one.\");\r\n\r\n            if (room.RoomID == null)\r\n                throw new InvalidOperationException(\"Cannot join an inactive room.\");\r\n\r\n            var cancellationSource = joinCancellationSource = new CancellationTokenSource();\r\n\r\n            await joinOrLeaveTaskChain.Add(async () =>\r\n            {\r\n                await runOnUpdateThreadAsync(() => pendingRequests.Clear(), cancellationSource.Token).ConfigureAwait(false);\r\n                var multiplayerRoom = await JoinRoomInternal(room.RoomID.Value, password ?? room.Password).ConfigureAwait(false);\r\n                await setupJoinedRoom(room, multiplayerRoom, cancellationSource.Token).ConfigureAwait(false);\r\n            }, cancellationSource.Token).ConfigureAwait(false);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Performs post-join setup of a <see cref=\"MultiplayerRoom\"/>.\r\n        /// </summary>\r\n        /// <param name=\"apiRoom\">The incoming API <see cref=\"Room\"/> that was requested to be joined.</param>\r\n        /// <param name=\"joinedRoom\">The resuling <see cref=\"MultiplayerRoom\"/> that was joined.</param>\r\n        /// <param name=\"cancellationToken\">A token to cancel the process.</param>\r\n        private async Task setupJoinedRoom(Room apiRoom, MultiplayerRoom joinedRoom, CancellationToken cancellationToken)\r","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/ppy/osu/blob/d9c73e12adff2feaae4a3e158d36fe5883faf6ca/osu.Game/Online/Multiplayer/MultiplayerClient.cs#L246-L282","documentation":"Thrown by MultiplayerClient.JoinRoom when the passed Room has no RoomID. A room must exist server-side (have been created) to be joinable; a locally constructed Room with null RoomID cannot be joined.","triggerScenarios":"Calling JoinRoom(room) where room.RoomID == null, i.e. using a Room instance that was never created via CreateRoom or fetched from the API room listings.","commonSituations":"Passing a freshly `new Room { Name = ... }` to JoinRoom instead of a Room object returned from the API listing endpoint; deserialization dropping the RoomID field.","solutions":["Only join rooms obtained from the API listing (CreateRoom result or a fetched Room) so RoomID is populated.","Guard: if (room.RoomID == null) reject the join in the UI.","Verify the API response deserialized RoomID correctly (check JSON property mapping)."],"exampleFix":"// before\nawait client.JoinRoom(new Room { Name = \"test\" });\n\n// after\nif (room.RoomID == null) throw new InvalidOperationException(\"room not created server-side\");\nawait client.JoinRoom(room);","handlingStrategy":"validation","validationCode":"if (room.RoomID == null)\n    throw new InvalidOperationException(\"Cannot join: room has no RoomID.\");\nawait client.JoinRoom(room, password);","typeGuard":"bool IsJoinable(Room room) => room.RoomID.HasValue;","tryCatchPattern":"try { await client.JoinRoom(room, password); }\ncatch (InvalidOperationException ex) { /* inactive room: require a created/listed Room */ }","preventionTips":["Only join Room instances returned from CreateRoom or the API listing.","Verify RoomID survived JSON deserialization.","Reject null-RoomID rooms in the UI before invoking join."],"tags":["multiplayer","room","validation","invalid-operation"],"backgroundTag":null,"analyzedSha":"d9c73e12adff2feaae4a3e158d36fe5883faf6ca","analyzedAt":"2026-08-13T14:12:54.015Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}