babalae/better-genshin-impact · critical · PartySetupFailedException

切换角色:当前角色状态为空

Error message

切换角色:当前角色状态为空

What it means

HandleFindAndClickAvatar asserts _currentRole is non-null. This is a state-machine invariant: the FindAndClickAvatar state should only be active after PrepareNextRole/OpenFilterPanel set a current role. A null here means the detectors routed to this handler with no role selected — a logic/transition bug rather than a normal runtime condition.

Source

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

        }

        _pendingFilterElementType = null;
        _pendingFilterWeaponType = null;
        _workflowState = SwitchCharacterState.FindAndClickAvatar;
        return Task.FromResult(StateHandlerResult.Success);
    }

    /// <summary>
    /// 查找并点击当前角色头像。
    /// </summary>
    /// <param name="page">页面操作对象。</param>
    /// <returns>头像查找并提交完成后进入准备下一个目标角色状态。</returns>
    [StateHandler(SwitchCharacterState.FindAndClickAvatar, RetryTimeout = 12000, RetryInterval = 300, TransitionTimeout = 3000)]
    private async Task<StateHandlerResult> HandleFindAndClickAvatar(BvPage page)
    {
        if (_currentRole == null)
        {
            throw new PartySetupFailedException("切换角色:当前角色状态为空");
        }

        var avatarFound = false;
        if (_currentRole.UseFriendshipSort)
        {
            await EnsureFriendshipSort(page);
            for (var attempt = 1; attempt <= FriendshipSortSearchAttemptCount; attempt++)
            {
                ClickSortDirectionButton();
                if (await FindAndClickAvatar(_currentRole, Recognizer, _ct))
                {
                    avatarFound = true;
                    break;
                }

                _logger.LogDebug(
                    "切换角色:好感排序第 {Attempt}/{AttemptCount} 次未找到 {Name}",
                    attempt,

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Treat as a bug: trace every assignment to _workflowState = SwitchCharacterState.FindAndClickAvatar and confirm each is preceded by SetCurrentRole.
  2. If a soft recovery is acceptable, return StateHandlerResult.Retry and re-run PrepareNextRole instead of throwing, so the machine re-picks a role.
  3. Add a unit test over the transition table proving FindAndClickAvatar is only reachable after a role is bound.

Example fix

// before
if (_currentRole == null)
{
    throw new PartySetupFailedException("切换角色:当前角色状态为空");
}

// after
if (_currentRole == null)
{
    _workflowState = SwitchCharacterState.PrepareNextRole;
    return StateHandlerResult.Retry;
}
Defensive patterns

Strategy: try-catch

Try / catch

try { await switchTask.Start(...); }
catch (PartySetupFailedException ex) when (ex.Message.Contains("当前角色状态为空"))
{ _logger.LogError(ex, "状态机逻辑异常:FindAndClickAvatar 在未选定角色时触发,请提交 bug"); }

Prevention

When it happens

Trigger: DetectFindAndClickAvatar returned true (workflowState == FindAndClickAvatar and IsCharacterList && !IsFilterPanel) while _currentRole was still null — e.g. PrepareNextRole deferred role selection and the workflowState was set to FindAndClickAvatar directly.

Common situations: A code change that sets _workflowState = FindAndClickAvatar without calling SetCurrentRole first; a rebuild/append path that clears _currentRole but leaves the workflow state pointing at avatar-finding; an unhandled transition into this state.

Related errors


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