{"record":{"id":"d18239274b9d417b","repo":"flipped-aurora/gin-vue-admin","slug":"d-d18239","errorCode":null,"errorMessage":"单次授权菜单数量不能超过 %d 个","messagePattern":"单次授权菜单数量不能超过 (.+?) 个","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mcp/role_menu_assigner.go","lineNumber":70,"sourceCode":"\t)\n}\n\nfunc (r *RoleMenuAssigner) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\targs := request.GetArguments()\n\n\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{","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/mcp/role_menu_assigner.go#L52-L88","documentation":"The role-menu assigner caps a single authorization call at `orgBatchLimit` menu IDs to avoid oversized payloads and long-running casbin writes. Exceeding the cap aborts before any upstream call. The message interpolates the configured maximum.","triggerScenarios":"Calling the assign-menus tool with a menuIds array longer than orgBatchLimit (e.g. attempting to grant every menu of the system in one call).","commonSituations":"Bulk-provisioning a new super-admin role from a script; migrating permissions between environments in one shot; generating menuIds from a wide query without chunking.","solutions":["Split menuIds into batches no larger than orgBatchLimit and call the tool once per batch","Pre-filter menuIds to only the menus actually needed (sub-menus often come along with parents automatically — the tool补齐父链)","If bulk setup is required, script multiple sequential calls and verify with a role-menu query tool"],"exampleFix":"// before\nassignMenus(authorityID, allMenuIDs) // 200 ids > limit\n// after\nfor batch := range chunk(allMenuIDs, orgBatchLimit) {\n    assignMenus(authorityID, batch)\n}","handlingStrategy":"validation","validationCode":"if len(menuIDs) > orgBatchLimit {\n    return fmt.Errorf(\"chunk menuIds: %d exceeds limit %d\", len(menuIDs), orgBatchLimit)\n}\nfor batch := range chunk(menuIDs, orgBatchLimit) {\n    if err := assignMenus(ctx, authorityID, batch); err != nil {\n        return err\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Chunk any menuIds list before calling","Only include menus the role actually needs","Script multi-batch assignment for bulk provisioning"],"tags":["mcp","validation","batch-limit"],"backgroundTag":"batch-size-exceeded","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}