{"record":{"id":"12383f334bc20b73","repo":"babalae/better-genshin-impact","slug":"slot-slot","errorCode":null,"errorMessage":"切换角色：{slot.Slot} 号位角色识别为空","messagePattern":"切换角色：(.+?) 号位角色识别为空","errorType":"exception","errorClass":"PartySetupFailedException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/Common/Job/SwitchCharacterStateMachineTask.cs","lineNumber":1035,"sourceCode":"                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /// <summary>\n    /// 将显式目标与当前队伍合并为完整的期望队伍，未指定角色优先保留原物理位置。\n    /// </summary>\n    private static List<TargetRole> BuildDesiredTeamRoles(\n        IReadOnlyCollection<TargetRole> requestedRoles,\n        IReadOnlyList<TeamSlotSnapshot> currentSlots)\n    {\n        var desired = new TargetRole?[currentSlots.Count];\n        var remaining = currentSlots\n            .Select(slot => (slot.Slot, Name: slot.Name\n                ?? throw new PartySetupFailedException($\"切换角色：{slot.Slot} 号位角色识别为空\")))\n            .ToList();\n\n        foreach (var role in requestedRoles.OrderBy(role => role.Slot))\n        {\n            desired[role.Slot - 1] = role;\n            var currentIndex = remaining.FindIndex(item => role.Matches(item.Name));\n            if (currentIndex >= 0)\n            {\n                remaining.RemoveAt(currentIndex);\n            }\n        }\n\n        for (var index = 0; index < desired.Length; index++)\n        {\n            if (desired[index] != null)\n            {\n                continue;\n            }","sourceCodeStart":1017,"sourceCodeEnd":1053,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/Common/Job/SwitchCharacterStateMachineTask.cs#L1017-L1053","documentation":"BuildDesiredTeamRoles needs every current slot's name to merge requested roles with the existing party, but a TeamSlotSnapshot had Name==null — meaning avatar recognition for that slot scored below MatchThreshold in RecognizeTeamSlotsFromCharacterList. Without a name the merge (remaining list) cannot be built, so it fails.","triggerScenarios":"RecognizeTeamSlotsFromCharacterList added a null entry to characterNames (candidate.Score < 0.7) which became a TeamSlotSnapshot with null Name; BuildDesiredTeamRoles then evaluates slot.Name ?? throw during the .Select projection.","commonSituations":"Avatar icon recognition confidence was low for one slot (lighting, costume, capture scale); the character grid partially occluded an avatar; recognizer model lacks that character's icon; transitional frame captured mid-scroll.","solutions":["Improve capture quality / resolution so recognition scores clear 0.7 for all slots.","Extend AvatarGridIconRecognizer with the missing or low-confidence character icon.","Re-run the recognition pass (trigger another _teamSnapshotDirty=true) before BuildDesiredTeamRoles so a fresh capture can resolve the null.","If a name genuinely cannot resolve, surface it earlier in RecognizeTeamSlotsFromCharacterList (error 394 path) with full context."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (currentSlots.Any(slot => string.IsNullOrWhiteSpace(slot.Name)))\n{\n    _teamSnapshotDirty = true; // force a fresh recognition pass\n    return; // re-recognize before building desired roles\n}","typeGuard":"static bool AllSlotsNamed(IReadOnlyList<TeamSlotSnapshot> slots)\n    => slots.All(slot => !string.IsNullOrWhiteSpace(slot.Name));","tryCatchPattern":"try { await switchTask.Start(...); }\ncatch (PartySetupFailedException ex) when (ex.Message.Contains(\"角色识别为空\"))\n{ _logger.LogWarning(ex, \"存在槽位头像识别失败，建议重试或改善截图质量\"); }","preventionTips":["Re-run recognition when any slot name is null instead of passing nulls into BuildDesiredTeamRoles.","Maintain capture quality/resolution so scores exceed 0.7.","Keep the recognizer model complete."],"tags":["party-setup","character-recognition","opencv","data-merge","switch-character"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}