flipped-aurora/gin-vue-admin · error

单次授权菜单数量不能超过 个

Error message

单次授权菜单数量不能超过 %d 个

What it means

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.

Solutions

  1. Split menuIds into batches no larger than orgBatchLimit and call the tool once per batch
  2. Pre-filter menuIds to only the menus actually needed (sub-menus often come along with parents automatically — the tool补齐父链)
  3. If bulk setup is required, script multiple sequential calls and verify with a role-menu query tool

Example fix

// before
assignMenus(authorityID, allMenuIDs) // 200 ids > limit
// after
for batch := range chunk(allMenuIDs, orgBatchLimit) {
    assignMenus(authorityID, batch)
}
Defensive patterns

Strategy: validation

Validate before calling

if len(menuIDs) > orgBatchLimit {
    return fmt.Errorf("chunk menuIds: %d exceeds limit %d", len(menuIDs), orgBatchLimit)
}
for batch := range chunk(menuIDs, orgBatchLimit) {
    if err := assignMenus(ctx, authorityID, batch); err != nil {
        return err
    }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/d18239274b9d417b. Report an issue: GitHub.

Appendix: source

Thrown at server/mcp/role_menu_assigner.go:70

	)
}

func (r *RoleMenuAssigner) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	args := request.GetArguments()

	authorityID, err := parseAuthorityID(args["authorityId"])
	if err != nil {
		return nil, err
	}
	menuIDs, err := parseUintList(args["menuIds"], "menuIds")
	if err != nil {
		return nil, err
	}
	if err := requireNonEmptyList(menuIDs, "menuIds"); err != nil {
		return nil, err
	}
	if len(menuIDs) > orgBatchLimit {
		return nil, fmt.Errorf("单次授权菜单数量不能超过 %d 个", orgBatchLimit)
	}

	// 全量基础菜单:存在性校验、父链补齐、写回时的完整菜单对象都从这里取
	allResp, err := postUpstream[[]system.SysBaseMenu](ctx, "/menu/getMenuList", map[string]any{})
	if err != nil {
		return nil, fmt.Errorf("获取菜单列表失败: %w", err)
	}
	menuIndex := make(map[uint]system.SysBaseMenu)
	flattenBaseMenus(allResp.Data, menuIndex)

	for _, id := range menuIDs {
		if _, ok := menuIndex[id]; !ok {
			return nil, fmt.Errorf("菜单 %d 不存在,可先用 list_all_menus 查询全部菜单", id)
		}
	}

	// 角色当前授权(先读,防覆盖丢权)
	currentResp, err := postUpstream[map[string][]system.SysMenu](ctx, "/menu/getMenuAuthority", map[string]any{

View on GitHub (pinned to 3136500ef3)