babalae/better-genshin-impact · error · PartySetupFailedException

切换角色:{logicalSlot} 号位超出当前账号可操作角色数 {_maxControlAvatarCount}

Error message

切换角色:{logicalSlot} 号位超出当前账号可操作角色数 {_maxControlAvatarCount}

What it means

EnsureSlotIsOperable checks that a logical slot exists in _logicalToPhysicalSlot (the controllable-slot map built by ConfigureOperableSlots). In co-op a player only owns some physical slots, so requesting a logical slot beyond _maxControlAvatarCount (e.g. 2P asking for logical slot 3) has no physical target and is rejected.

Source

Thrown at BetterGenshinImpact/GameTask/Common/Job/SwitchCharacterStateMachineTask.cs:1272

            (4, 4) => [4],
            _ => throw new PartySetupFailedException(
                $"切换角色:无法为 {_multiGamePlayerCount} 人联机的 {_playerIndex}P 生成可控槽位")
        };

        _maxControlAvatarCount = physicalSlots.Length;
        _logicalToPhysicalSlot = physicalSlots
            .Select((physical, index) => (Logical: index + 1, Physical: physical))
            .ToDictionary(pair => pair.Logical, pair => pair.Physical);

        _logger.LogInformation("切换角色:{PlayerCount} 人队伍,{PlayerIndex}P 可控物理槽位 {Slots}",
            _multiGamePlayerCount, _playerIndex, string.Join(",", physicalSlots));
    }

    private void EnsureSlotIsOperable(int logicalSlot)
    {
        if (!_logicalToPhysicalSlot.ContainsKey(logicalSlot))
        {
            throw new PartySetupFailedException($"切换角色:{logicalSlot} 号位超出当前账号可操作角色数 {_maxControlAvatarCount}");
        }
    }

    private void AdjustTargetsToOperableSlots()
    {
        int[] ignoredSlots;
        if (_usePhysicalSlots)
        {
            var logicalByPhysicalSlot = _logicalToPhysicalSlot
                .ToDictionary(pair => pair.Value, pair => pair.Key);
            ignoredSlots = _targetRoles
                .Where(role => !logicalByPhysicalSlot.ContainsKey(role.Slot))
                .Select(role => role.Slot)
                .OrderBy(slot => slot)
                .ToArray();

            _targetRoles = _targetRoles
                .Where(role => logicalByPhysicalSlot.ContainsKey(role.Slot))

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Rely on AdjustTargetsToOperableSlots to strip/ignore slots the current player cannot operate before any click — confirm it ran and filtered the offending role.
  2. When scripting for co-op, only request slots within the player's controllable range (documented in Start's remarks).
  3. Call EnsureSlotIsOperable earlier (during BuildSwitchPlan) to fail with context before attempting UI clicks.
Defensive patterns

Strategy: validation

Validate before calling

if (!_logicalToPhysicalSlot.ContainsKey(role.Slot))
{
    _logger.LogWarning("切换角色:忽略不可操作槽位 {Slot}", role.Slot);
    continue;
}

Type guard

bool IsOperable(int logicalSlot) => _logicalToPhysicalSlot.ContainsKey(logicalSlot);

Try / catch

try { await switchTask.Start(...); }
catch (PartySetupFailedException ex) when (ex.Message.Contains("超出当前账号可操作角色数"))
{ _logger.LogWarning(ex, "联机模式下请求了不可控槽位,已忽略"); }

Prevention

When it happens

Trigger: EnsureSlotIsOperable(logicalSlot) is called (from ClickFixedTeamSlot or HandlePrepareNextRole) with a logicalSlot not present in _logicalToPhysicalSlot, which happens when the requested target role's slot exceeds the controllable count for this player in co-op.

Common situations: usePhysicalSlots=false and a target role references a slot index beyond _maxControlAvatarCount; usePhysicalSlots=true but AdjustTargetsToOperableSlots did not filter/remap it; a co-op player issued a 4-slot command but only controls 1-2 slots.

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/980f0dbf929455f5. Report an issue: GitHub.