{"record":{"id":"9f5062065ee75185","repo":"babalae/better-genshin-impact","slug":"refillrole-slot-maxroleswitchattempts","errorCode":null,"errorMessage":"切换角色：{refillRole.Slot} 号位连续 {MaxRoleSwitchAttempts} 次未能补回 {refillRole.Name}","messagePattern":"切换角色：(.+?) 号位连续 (.+?) 次未能补回 (.+?)","errorType":"exception","errorClass":"PartySetupFailedException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/Common/Job/SwitchCharacterStateMachineTask.cs","lineNumber":636,"sourceCode":"                if (!TryClickText(page, \"换下\", Rect1080(382, 994, 87, 51)))\n                {\n                    throw new PartySetupFailedException($\"切换角色：未找到 {rebuildStartSlot} 号位的换下按钮\");\n                }\n\n                await WaitForRoleRemoved(rebuildStartSlot, _ct);\n                _expectedTeamCount--;\n                _teamSnapshotDirty = true;\n                return StateHandlerResult.Wait;\n            }\n\n            _isRebuildClearing = false;\n\n            if (_rebuildRoles.Count > 0)\n            {\n                var refillRole = _rebuildRoles.Peek();\n                if (_roleSwitchAttempts.GetValueOrDefault(refillRole.Slot) >= MaxRoleSwitchAttempts)\n                {\n                    throw new PartySetupFailedException(\n                        $\"切换角色：{refillRole.Slot} 号位连续 {MaxRoleSwitchAttempts} 次未能补回 {refillRole.Name}\");\n                }\n\n                SetCurrentRole(refillRole);\n                _isAppendingRole = refillRole.Slot > _currentTeamSlots.Count;\n                ClickFixedTeamSlot(refillRole.Slot);\n                _workflowState = SwitchCharacterState.OpenFilterPanel;\n                return StateHandlerResult.Success;\n            }\n\n            _rebuildStartSlot = null;\n        }\n\n        if (_currentRole == null)\n        {\n            var currentNameBySlot = _currentTeamSlots.ToDictionary(slot => slot.Slot, slot => slot.Name);\n            var nextRole = _requestedTargetRoles\n                .OrderBy(role => role.Slot)","sourceCodeStart":618,"sourceCodeEnd":654,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/Common/Job/SwitchCharacterStateMachineTask.cs#L618-L654","documentation":"Thrown during the suffix-rebuild refill phase when a single slot has exhausted its retry budget: _roleSwitchAttempts for the peeked refillRole.Slot has reached MaxRoleSwitchAttempts (3). The role could not be added back to the team after 3 consecutive attempts.","triggerScenarios":"In HandlePrepareNextRole, _rebuildRoles.Peek() is a refillRole and _roleSwitchAttempts[refillRole.Slot] >= 3. Each prior OpenFilterPanel/select/save cycle for that slot failed to land the named role, incrementing the attempt counter until the cap.","commonSituations":"The named role is not owned/unlocked; the role's filter (element/weapon) is wrong so the avatar is never shown; the recognizer mis-identifies the placed avatar so Matches() stays false; UI lag drops a confirm click; an alias resolves to a name not present in the player's roster.","solutions":["Confirm the role name resolves to an avatar the account actually owns.","Verify SetCurrentRoleFilter element/weapon detection (Recognizer.GetElementType/GetWeaponType) for that role — a wrong filter hides it.","Increase MaxRoleSwitchAttempts for resilient runs, or surface a recoverable error instead of aborting the whole switch.","Log each attempt's recognized slot name to see why Matches() keeps failing."],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"// Validate roster ownership before starting the switch\nforeach (var name in new[] { slot1, slot2, slot3, slot4 }.Where(s => !string.IsNullOrWhiteSpace(s)))\n{\n    var standard = ToConfiguredAvatarName(name.Trim());\n    if (!DefaultAutoFightConfig.CombatAvatarMap.ContainsKey(standard) && !IsOwnedAvatar(standard))\n        throw new ArgumentException($\"账号未拥有角色：{standard}\");\n}","typeGuard":"static bool RoleIsOwned(string name)\n{\n    var standard = ToConfiguredAvatarName(name.Trim());\n    return DefaultAutoFightConfig.CombatAvatarMap.ContainsKey(standard) || IsOwnedAvatar(standard);\n}","tryCatchPattern":"try { await sm.Start(s1, s2, s3, s4, usePhysicalSlots, ct); }\ncatch (PartySetupFailedException e) when (e.Message.Contains(\"未能补回\"))\n{ /* skip the unattainable slot and continue with remaining roles, or alert the user */ }","preventionTips":["Confirm the account owns every requested role before switching.","Verify element/weapon filter detection for each role so the grid shows it.","Log recognized slot names each attempt to spot recognizer false negatives."],"tags":["party-setup","state-machine","retry-budget","recognition"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}