{"record":{"id":"276e16e08c4cf0df","repo":"Billionmail/BillionMail","slug":"invalid-end-date-v","errorCode":null,"errorMessage":"invalid end_date: %v","messagePattern":"invalid end_date: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/operation_log/operation_log_v1_get_output_log.go","lineNumber":117,"sourceCode":"}\n\nfunc reverseLines(lines []string) {\n\tfor i, j := 0, len(lines)-1; i < j; i, j = i+1, j-1 {\n\t\tlines[i], lines[j] = lines[j], lines[i]\n\t}\n}\n\nfunc getLogFilesInRange(ctx context.Context, start, end string) ([]string, error) {\n\tlogDir := public.AbsPath(\"../logs/core/out\")\n\tlayout := \"2006-01-02\"\n\n\tstartDate, err := time.Parse(layout, start)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid start_date: %v\", err)\n\t}\n\tendDate, err := time.Parse(layout, end)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid end_date: %v\", err)\n\t}\n\tif endDate.Before(startDate) {\n\t\treturn nil, fmt.Errorf(\"end_date must not be before start_date\")\n\t}\n\n\tvar files []string\n\n\tfor d := startDate; !d.After(endDate); d = d.AddDate(0, 0, 1) {\n\t\tfname := filepath.Join(logDir, d.Format(\"2006-01-02\")+\".log\")\n\t\tif _, err := os.Stat(fname); err == nil {\n\t\t\tfiles = append(files, fname)\n\t\t}\n\t}\n\n\tif len(files) == 0 {\n\t\treturn nil, fmt.Errorf(\"no log files found\")\n\t}\n\treturn files, nil","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/operation_log/operation_log_v1_get_output_log.go#L99-L135","documentation":"Identical to the start_date check but for end_date: time.Parse with layout '2006-01-02' fails and the error is wrapped as 'invalid end_date: %v'. After both parse, an inverted range is rejected separately ('end_date must not be before start_date').","triggerScenarios":"Calling GetOutputLog with an end_date not in YYYY-MM-DD form: empty string, slashes, ISO datetime, or typo like '2026-9-5'.","commonSituations":"Client sends 'now' literal or datetime string for the end bound; locale-formatted dates; the end date field left blank while start_date is filled.","solutions":["Format end_date strictly as YYYY-MM-DD.","Ensure end_date >= start_date so the follow-on range check does not fire either.","Validate both dates client-side with a strict regex (^\\d{4}-\\d{2}-\\d{2}$) before the call."],"exampleFix":"// before\n{\"start_date\":\"2026-08-01\",\"end_date\":\"\"}\n// after\n{\"start_date\":\"2026-08-01\",\"end_date\":\"2026-09-05\"}","handlingStrategy":"validation","validationCode":"function isValidDate(s) {\n  if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(s)) return false;\n  const d = new Date(s + 'T00:00:00Z');\n  return !isNaN(d.getTime()) && d.toISOString().slice(0, 10) === s;\n}\nif (!isValidDate(endDate) || endDate < startDate) throw new Error('end_date must be valid YYYY-MM-DD and not before start_date');","typeGuard":null,"tryCatchPattern":"try {\n  return await api.getOutputLog({startDate, endDate})\n} catch (e) {\n  if (/invalid end_date/.test(e.message)) {\n    throw new UserInputError('end_date must match YYYY-MM-DD')\n  }\n  throw e\n}","preventionTips":["Default end_date to today (YYYY-MM-DD) when the field is blank.","Validate both bounds and their ordering together before the request.","Strip any time component from datetime inputs for the end bound."],"tags":["validation","dates","api"],"backgroundTag":"invalid-date-format","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}