babalae/better-genshin-impact · warning · PartySetupFailedException

切换角色:未指定角色或同一实际角色被指定到多个槽位

Error message

切换角色:未指定角色或同一实际角色被指定到多个槽位

What it means

Thrown by SwitchCharacterStateMachineTask.Start when ParseRoles yields zero target roles, or when HasConflictingRoleTargets detects that two slots resolve to the same underlying character (overlap in ConflictNames). It is an input-validation guard before any UI work begins.

Source

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

    /// 3 人联机时 1P 可操作 1、2 号槽位,2P 可操作 3 号槽位,3P 可操作 4 号槽位;
    /// 4 人联机时各玩家可操作与玩家编号相同的槽位。当前玩家不可操作的槽位参数会被忽略。
    /// </remarks>
    public async Task<bool> Start(
        string slot1,
        string slot2,
        string slot3,
        string slot4,
        bool usePhysicalSlots,
        CancellationToken ct)
    {
        Initialize(ct, SwitchCharacterState.Unknown);
        var page = new BvPage(ct);
        string[] slots = [slot1, slot2, slot3, slot4];

        var roles = ParseRoles(slots);
        if (roles.Count == 0 || HasConflictingRoleTargets(roles))
        {
            throw new PartySetupFailedException("切换角色:未指定角色或同一实际角色被指定到多个槽位");
        }

        ResetWorkflow(roles, usePhysicalSlots);
        using var recognizer = new AvatarGridIconRecognizer();
        _recognizer = recognizer;

        try
        {
            await RunStateMachineUntil(page, SwitchCharacterState.Completed);
            return true;
        }
        finally
        {
            _recognizer = null;
        }
    }

    /// <summary>

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Ensure at least one slot is a non-empty role name.
  2. Do not place the Traveler (空/荧/旅行者) in more than one slot — they are one underlying character.
  3. Validate slots up front: distinct standard names, at least one non-empty.
  4. Check AvatarAliasToStandardName / CombatAvatarMap for alias collisions if a custom alias set is configured.

Example fix

// before
var roles = ParseRoles(slots);
if (roles.Count == 0 || HasConflictingRoleTargets(roles))
{
    throw new PartySetupFailedException("切换角色:未指定角色或同一实际角色被指定到多个槽位");
}

// after: report which condition failed for faster diagnosis
var roles = ParseRoles(slots);
if (roles.Count == 0)
    throw new PartySetupFailedException("切换角色:未指定任何角色");
if (HasConflictingRoleTargets(roles))
    throw new PartySetupFailedException("切换角色:同一实际角色被指定到多个槽位");
Defensive patterns

Strategy: validation

Validate before calling

string[] slots = [slot1, slot2, slot3, slot4];
var names = slots.Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToList();
if (names.Count == 0)
    throw new ArgumentException("至少需要指定一个角色槽位");
// reject Traveler placed in two slots
var travelerCount = slots.Count(s => ToConfiguredAvatarName(s.Trim()) == TravelerAliasName);
if (travelerCount > 1)
    throw new ArgumentException("旅行者只能占据一个槽位");

Type guard

static bool IsValidPartySetup(string s1, string s2, string s3, string s4)
{
    var names = new[] { s1, s2, s3, s4 }.Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToList();
    return names.Count > 0 && names.Distinct().Count() == names.Count;
}

Try / catch

null

Prevention

When it happens

Trigger: slots = [slot1..slot4]; after trimming, no non-empty names remain (roles.Count == 0), OR two roles share a ConflictNames entry — e.g. slot1='空' and slot2='荧' both map to the Traveler whose ConflictNames are [PlayerBoyName, PlayerGirlName], so Intersect is non-empty.

Common situations: Caller passed all-empty/whitespace slots; caller assigned the Traveler (or a '空'/'荧' alias pair) to two slots; an alias resolves (via AvatarAliasToStandardName) to the same standard name across slots; a misconfigured combat avatar map aliases two names to one character.

Related errors


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