{"record":{"id":"903abcb10cf97c81","repo":"flipped-aurora/gin-vue-admin","slug":"deptids-positionids","errorCode":null,"errorMessage":"deptIds 与 positionIds 至少需要传一个","messagePattern":"deptIds 与 positionIds 至少需要传一个","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mcp/org_member_assigner.go","lineNumber":90,"sourceCode":"func (o *OrgMemberAssigner) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\targs := request.GetArguments()\n\n\tuserID, err := parseUintParam(args[\"userId\"], \"userId\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tusernameHint, _ := args[\"username\"].(string)\n\n\tdeptIDs, err := parseUintList(args[\"deptIds\"], \"deptIds\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpositionIDs, err := parseUintList(args[\"positionIds\"], \"positionIds\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(deptIDs) == 0 && len(positionIDs) == 0 {\n\t\treturn nil, errors.New(\"deptIds 与 positionIds 至少需要传一个\")\n\t}\n\tif len(deptIDs) > orgBatchLimit || len(positionIDs) > orgBatchLimit {\n\t\treturn nil, fmt.Errorf(\"单次分配的部门/岗位数量各不能超过 %d 个\", orgBatchLimit)\n\t}\n\t// primaryDeptId 只在设置部门(deptIds)时生效;仅传 primaryDeptId 不传 deptIds 会被静默丢弃,\n\t// 显式挡回避免调用方误以为主部门已变更\n\tif value, ok := args[\"primaryDeptId\"]; ok && value != nil && len(deptIDs) == 0 {\n\t\treturn nil, errors.New(\"primaryDeptId 需与 deptIds 一起传入(主部门必须属于本次追加或已有的部门集合);仅调整主部门请到前端组织管理页操作\")\n\t}\n\n\t// 校验目标部门/岗位真实存在,把无效ID挡在写入之前\n\tif len(deptIDs) > 0 {\n\t\tdeptIndex, err := fetchDeptIndex(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, id := range deptIDs {\n\t\t\tif _, ok := deptIndex[id]; !ok {","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/mcp/org_member_assigner.go#L72-L108","documentation":"The org_member_assigner MCP tool assigns members to departments and/or positions; it requires at least one of the 'deptIds' or 'positionIds' lists. If both parsed lists are empty the tool refuses to proceed rather than performing a no-op write. A secondary limit check follows: each list may contain at most orgBatchLimit entries per call.","triggerScenarios":"Calling org_member_assigner with neither deptIds nor positionIds; passing both as empty arrays; passing values that parseUintList rejected so both lists end up empty (e.g. non-array types or all-non-numeric entries); only providing primaryDeptId while omitting deptIds.","commonSituations":"Scripts that conditionally build one list but never the other; callers assuming primaryDeptId alone changes the primary department (it is explicitly rejected without deptIds); oversized batches that should have been split (see the sibling batch-limit error).","solutions":["Include at least one ID in deptIds or positionIds","If you only intended to set the primary department, pass it together with deptIds (or use the frontend org management page for a primary-only change)","Send both parameters as JSON arrays of numeric IDs, not strings or objects","Split very large assignments into multiple calls respecting the orgBatchLimit"],"exampleFix":"// before\nargs := map[string]interface{}{ \"username\": \"alice\", \"primaryDeptId\": 3 }\n// after\nargs := map[string]interface{}{ \"username\": \"alice\", \"deptIds\": []uint{3}, \"primaryDeptId\": 3 }","handlingStrategy":"validation","validationCode":"func validAssignArgs(args map[string]interface{}) error {\n  d, _ := args[\"deptIds\"].([]interface{})\n  p, _ := args[\"positionIds\"].([]interface{})\n  if len(d) == 0 && len(p) == 0 {\n    return fmt.Errorf(\"provide at least one of deptIds or positionIds\")\n  }\n  return nil\n}","typeGuard":"func hasAssignTarget(args map[string]interface{}) bool {\n  d, _ := args[\"deptIds\"].([]interface{})\n  p, _ := args[\"positionIds\"].([]interface{})\n  return len(d) > 0 || len(p) > 0\n}","tryCatchPattern":"_, err := callTool(ctx, \"org_member_assigner\", args)\nif err != nil && strings.Contains(err.Error(), \"至少需要传一个\") {\n  return fmt.Errorf(\"assignment skipped: no deptIds/positionIds supplied: %w\", err)\n}","preventionTips":["Ensure conditional payload builders always emit at least one list","Remember primaryDeptId alone is not a valid target","Respect the per-call batch limit by splitting large sets","Validate list types (array of numbers) before sending"],"tags":["mcp","parameter-validation","go","org-management"],"backgroundTag":"missing-required-parameter","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}