ppy/osu · error · InvalidOperationException

Must be joined to a match to change settings.

Error message

Must be joined to a match to change settings.

What it means

Thrown by MultiplayerClient.ChangeSettings when Room is null, meaning the local user is not currently joined to a match. Settings can only be changed against an active joined room.

Source

Thrown at osu.Game/Online/Multiplayer/MultiplayerClient.cs:411

        /// <summary>
        /// Change the current <see cref="MultiplayerRoom"/> settings.
        /// </summary>
        /// <remarks>
        /// A room must be joined for this to have any effect.
        /// </remarks>
        /// <param name="name">The new room name, if any.</param>
        /// <param name="password">The new password, if any.</param>
        /// <param name="matchType">The type of the match, if any.</param>
        /// <param name="queueMode">The new queue mode, if any.</param>
        /// <param name="autoStartDuration">The new auto-start countdown duration, if any.</param>
        /// <param name="autoSkip">The new auto-skip setting.</param>
        /// <param name="maxParticipants">The new participant count limit, if any.</param>
        public Task ChangeSettings(Optional<string> name = default, Optional<string> password = default, Optional<MatchType> matchType = default, Optional<QueueMode> queueMode = default,
                                   Optional<TimeSpan> autoStartDuration = default, Optional<bool> autoSkip = default, Optional<byte?> maxParticipants = default)
        {
            if (Room == null)
                throw new InvalidOperationException("Must be joined to a match to change settings.");

            return ChangeSettings(new MultiplayerRoomSettings
            {
                Name = name.GetOr(Room.Settings.Name),
                Password = password.GetOr(Room.Settings.Password),
                MatchType = matchType.GetOr(Room.Settings.MatchType),
                QueueMode = queueMode.GetOr(Room.Settings.QueueMode),
                AutoStartDuration = autoStartDuration.GetOr(Room.Settings.AutoStartDuration),
                AutoSkip = autoSkip.GetOr(Room.Settings.AutoSkip),
                MaxParticipants = maxParticipants.GetOr(Room.Settings.MaxParticipants),
            });
        }

        /// <summary>
        /// Toggles the <see cref="LocalUser"/>'s ready state.
        /// </summary>
        /// <exception cref="InvalidOperationException">If a toggle of ready state is not valid at this time.</exception>
        public async Task ToggleReady()

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Ensure the client is joined (client.Room != null) before invoking ChangeSettings.
  2. Re-acquire the room reference or re-join if Room is null.
  3. Disable settings editing controls when client.Room == null.

Example fix

// before
await client.ChangeSettings(name: "New Name");

// after
if (client.Room == null) return;
await client.ChangeSettings(name: "New Name");
Defensive patterns

Strategy: validation

Validate before calling

if (client.Room == null) return;
await client.ChangeSettings(name: newName);

Type guard

bool CanEditSettings(MultiplayerClient c) => c.Room != null;

Try / catch

try { await client.ChangeSettings(settings); }
catch (InvalidOperationException) { /* not joined: ignore or re-prompt join */ }

Prevention

When it happens

Trigger: Calling ChangeSettings(...) before JoinRoom completes, or after the client has been disconnected/parted (Room cleared). The convenience overload builds a MultiplayerRoomSettings from Room.Settings and needs Room to exist.

Common situations: Race between leaving a room and a pending settings edit; UI letting the host edit settings after a kick/disconnect; calling ChangeSettings on a client that never joined.

Related errors


AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13). Data as JSON: /api/errors/3735c0b233058147. Report an issue: GitHub.