{"record":{"id":"21187d9cc47d9ff7","repo":"flipped-aurora/gin-vue-admin","slug":"w-21187d","errorCode":null,"errorMessage":"获取菜单列表失败: %w","messagePattern":"获取菜单列表失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mcp/role_menu_assigner.go","lineNumber":76,"sourceCode":"\tauthorityID, err := parseAuthorityID(args[\"authorityId\"])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmenuIDs, err := parseUintList(args[\"menuIds\"], \"menuIds\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := requireNonEmptyList(menuIDs, \"menuIds\"); err != nil {\n\t\treturn nil, err\n\t}\n\tif len(menuIDs) > orgBatchLimit {\n\t\treturn nil, fmt.Errorf(\"单次授权菜单数量不能超过 %d 个\", orgBatchLimit)\n\t}\n\n\t// 全量基础菜单:存在性校验、父链补齐、写回时的完整菜单对象都从这里取\n\tallResp, err := postUpstream[[]system.SysBaseMenu](ctx, \"/menu/getMenuList\", map[string]any{})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"获取菜单列表失败: %w\", err)\n\t}\n\tmenuIndex := make(map[uint]system.SysBaseMenu)\n\tflattenBaseMenus(allResp.Data, menuIndex)\n\n\tfor _, id := range menuIDs {\n\t\tif _, ok := menuIndex[id]; !ok {\n\t\t\treturn nil, fmt.Errorf(\"菜单 %d 不存在,可先用 list_all_menus 查询全部菜单\", id)\n\t\t}\n\t}\n\n\t// 角色当前授权(先读,防覆盖丢权)\n\tcurrentResp, err := postUpstream[map[string][]system.SysMenu](ctx, \"/menu/getMenuAuthority\", map[string]any{\n\t\t\"authorityId\": authorityID,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"获取角色当前菜单授权失败: %w\", err)\n\t}\n\tcurrentIDs := make([]uint, 0)","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/mcp/role_menu_assigner.go#L58-L94","documentation":"After the batch-size check, the assigner fetches the full base-menu tree via POST `/menu/getMenuList` to validate IDs, complete parent chains, and build write-back payloads. Any failure of that upstream call is wrapped with this message. Without the menu list no assignment can proceed.","triggerScenarios":"POST /menu/getMenuList fails: server unreachable, 401 on expired token, route missing, or server error while listing menus.","commonSituations":"gva backend down or restarting; JWT expired mid-session; database connectivity issue on the server side making menu listing fail; wrong upstream base URL.","solutions":["Check the wrapped cause for network vs upstream API error","Confirm the gva server is running and the menu API responds (curl /menu/getMenuList)","Refresh auth token if 401","Inspect server logs for errors in menu listing (often DB-related)"],"exampleFix":"// before\nallResp, err := postUpstream[[]system.SysBaseMenu](ctx, \"/menu/getMenuList\", map[string]any{})\nif err != nil {\n    return nil, fmt.Errorf(\"获取菜单列表失败: %w\", err)\n}\n// after\nallResp, err := postUpstream[[]system.SysBaseMenu](ctx, \"/menu/getMenuList\", map[string]any{})\nif err != nil {\n    log.Printf(\"getMenuList failed: %v\", err)\n    return nil, fmt.Errorf(\"获取菜单列表失败: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// health-check the menu endpoint before assignment\nresp, err := postUpstream[[]system.SysBaseMenu](ctx, \"/menu/getMenuList\", map[string]any{})\nif err != nil || len(resp.Data) == 0 {\n    return fmt.Errorf(\"menu source unavailable\")\n}","typeGuard":null,"tryCatchPattern":"err := retry(3, backoff, func() error {\n    _, err := assignMenus(ctx, authorityID, menuIDs)\n    return err\n})","preventionTips":["Ensure the gva backend is reachable before batch operations","Refresh tokens proactively","Check server DB health if menu listing fails repeatedly"],"tags":["mcp","upstream-api","menu"],"backgroundTag":"upstream-request-failed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}