{"record":{"id":"cbea25037635d140","repo":"Billionmail/BillionMail","slug":"end-time-must-greater-than-start-time","errorCode":null,"errorMessage":"end_time must greater than start_time","messagePattern":"end_time must greater than start_time","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/batch_mail/stat_service.go","lineNumber":67,"sourceCode":"\n\treturn map[string]interface{}{\n\t\t\"dashboard\":         s.getTaskDashboard(taskId, domain, startTime, endTime),\n\t\t\"mail_providers\":    s.getTaskMailProviders(taskId, domain, startTime, endTime),\n\t\t\"send_mail_chart\":   s.getTaskSendMailChart(taskId, domain, startTime, endTime),\n\t\t\"bounce_rate_chart\": s.getTaskBounceRateChart(taskId, domain, startTime, endTime),\n\t\t\"open_rate_chart\":   s.getTaskOpenRateChart(taskId, domain, startTime, endTime),\n\t\t\"click_rate_chart\":  s.getTaskClickRateChart(taskId, domain, startTime, endTime),\n\t}\n}\n\n// filterAndPrepareTimeSection\nfunc (s *TaskStatService) filterAndPrepareTimeSection(startTime, endTime int64) (int64, int64) {\n\tif startTime > 0 && endTime < 0 {\n\t\tendTime = time.Now().Unix()\n\t}\n\n\tif startTime > endTime {\n\t\tpanic(public.Lang(\"end_time must greater than start_time\"))\n\t}\n\n\treturn startTime, endTime\n}\n\n// prepareChartData\nfunc (s *TaskStatService) prepareChartData(startTime, endTime int64) (string, string) {\n\tcolumnType := \"daily\"\n\tsecs := endTime - startTime\n\n\txAxisField := \"EXTRACT(EPOCH FROM date_trunc('day', to_timestamp(sm.log_time_millis / 1000)))::bigint as x\"\n\tif secs < 86400 {\n\t\tcolumnType = \"hourly\"\n\t\txAxisField = \"to_char(to_timestamp(sm.log_time_millis / 1000), 'HH24')::integer as x\"\n\t}\n\n\treturn columnType, xAxisField\n}","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/batch_mail/stat_service.go#L49-L85","documentation":"TaskStatService.filterAndPrepareTimeSection validates the chart time window used by GetTaskStatChart. If startTime exceeds endTime it panics with a localized message via public.Lang, because a negative or inverted range would produce meaningless chart buckets. Note the odd guard: endTime is only defaulted to now when endTime < 0, so an unset (0) endTime does not protect against inversion.","triggerScenarios":"Calling GetTaskStatChart with startTime > endTime, e.g. startTime=1700000000, endTime=0 (endTime only auto-set when negative), or the caller swapping the two parameters.","commonSituations":"Frontend sends start/end in milliseconds while the backend expects Unix seconds (one value dwarfs the other); an empty end-date field submitted as 0; date pickers allowing end before start.","solutions":["Fix the caller to pass startTime <= endTime in Unix seconds.","Normalize units before calling (divide ms timestamps by 1000).","Validate/swapon the UI side: require an end date and enforce end >= start in the form.","Harden filterAndPrepareTimeSection to default endTime to now when endTime <= 0, since 0 is the common 'unset' value."],"exampleFix":"// before\nif startTime > 0 && endTime < 0 {\n    endTime = time.Now().Unix()\n}\n// after\nif startTime > 0 && endTime <= 0 {\n    endTime = time.Now().Unix()\n}","handlingStrategy":"validation","validationCode":"// Go: caller-side guard before GetTaskStatChart\nif start > 0 && (end <= 0 || start > end) {\n    return fmt.Errorf(\"invalid range: start=%d end=%d\", start, end)\n}","typeGuard":"func validTimeRange(start, end int64) bool {\n    return start > 0 && end > 0 && start <= end\n}","tryCatchPattern":"// the service panics, so recover at the handler boundary\ndefer func() {\n    if r := recover(); r != nil {\n        response.Fail(ctx, fmt.Sprintf(\"%v\", r))\n    }\n}()\nsvc.GetTaskStatChart(ctx, start, end)","preventionTips":["Convert client timestamps to Unix seconds before calling chart endpoints.","Default an unset end date to time.Now().Unix() in the handler.","Enforce end >= start in the date-picker UI.","Prefer returning an error over panic in new service code."],"tags":["validation","time-range","panic","batch-mail"],"backgroundTag":"invalid-time-range","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"}