{"record":{"id":"5bf5aa1124434dfe","repo":"Billionmail/BillionMail","slug":"threads-must-be-less-than-100","errorCode":null,"errorMessage":"threads must be less than 100","messagePattern":"threads must be less than 100","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/batch_mail/task_executor.go","lineNumber":1465,"sourceCode":"\treturn map[string]interface{}{\n\t\t\"sent_count\":    sent,\n\t\t\"failed_count\":  failed,\n\t\t\"total_count\":   total,\n\t\t\"success_rate\":  successRate,\n\t\t\"current_speed\": e.rateController.GetCurrentRate(),\n\t\t\"max_rate\":      e.rateController.GetMaxRate(),\n\t\t\"duration_sec\":  duration,\n\t}\n}\n\nfunc (e *TaskExecutor) UpdateTaskThreads(taskId int, threads int) error {\n\t// parameter validation\n\tif threads <= 0 {\n\t\treturn fmt.Errorf(\"threads must be greater than zero\")\n\t}\n\n\tif threads > 100 {\n\t\treturn fmt.Errorf(\"threads must be less than 100\")\n\t}\n\n\t// get task info\n\ttask, err := GetTaskInfo(context.Background(), taskId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get task info failed: %w\", err)\n\t}\n\n\tif task == nil || task.Id == 0 {\n\t\treturn fmt.Errorf(\"task %d not found\", taskId)\n\t}\n\n\t// record current pool status\n\tvar oldPoolSize int\n\tvar runningWorkers int\n\tif e.pool != nil {\n\t\toldPoolSize = e.pool.Cap()\n\t\trunningWorkers = e.pool.Running()","sourceCodeStart":1447,"sourceCodeEnd":1483,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/batch_mail/task_executor.go#L1447-L1483","documentation":"UpdateTaskThreads caps the worker-thread count at 100. Requesting more than 100 threads is rejected to prevent unbounded goroutine creation and resource exhaustion on the sending pool. The check runs before task lookup and pool resize.","triggerScenarios":"Calling UpdateTaskThreads(taskId, n) with n > 100, e.g. a client trying to max out sending speed or an unvalidated query/body parameter.","commonSituations":"Admin UIs without an upper bound; API consumers guessing at allowed limits; automation scripts scaling threads without knowing the cap.","solutions":["Use a threads value of 100 or less.","Clamp the input in the handler: if threads > 100 { threads = 100 } or return a 400 stating the allowed range 1-100.","Document the 1-100 range in the API/UI so clients don't attempt higher values."],"exampleFix":"// before\nawait api.updateTaskThreads(taskId, 500)\n// after\nconst threads = Math.min(Math.max(requested, 1), 100)\nawait api.updateTaskThreads(taskId, threads)","handlingStrategy":"validation","validationCode":"// Go\nconst maxThreads = 100\nif threads > maxThreads {\n    threads = maxThreads // or reject, per product rules\n}","typeGuard":"// TypeScript (client)\nconst threads = Math.min(Math.max(Number(input) || 1, 1), 100)\nexport function clampThreads(n: number): number { return Math.min(Math.max(n, 1), 100) }","tryCatchPattern":"if err := executor.UpdateTaskThreads(taskId, threads); err != nil {\n    if strings.Contains(err.Error(), \"less than 100\") {\n        return executor.UpdateTaskThreads(taskId, 100)\n    }\n    return err\n}","preventionTips":["Clamp thread inputs to 1-100 in both UI and API layer.","Publish the limit in API docs and the admin UI slider bounds.","Log clamping events so requests asking for oversized values are visible."],"tags":["validation","concurrency","limits","batch-mail"],"backgroundTag":"parameter-validation-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}