{"record":{"id":"5925292054b34ebf","repo":"koala73/worldmonitor","slug":"start-date-must-not-be-after-end-date","errorCode":null,"errorMessage":"start_date must not be after end_date","messagePattern":"start_date must not be after end_date","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"server/worldmonitor/intelligence/v1/search-sec-filings.ts","lineNumber":49,"sourceCode":"  const startDateValid = !req.startDate || isEdgarIsoDate(req.startDate);\n  const endDateValid = !req.endDate || isEdgarIsoDate(req.endDate);\n  const violations = [\n    ...(formsNormalized === null\n      ? [{ field: 'forms', description: 'forms must be a comma-separated form list such as \"8-K\" or \"10-K,10-Q\"' }]\n      : []),\n    ...(!startDateValid\n      ? [{ field: 'start_date', description: 'start_date must be YYYY-MM-DD' }]\n      : []),\n    ...(!endDateValid\n      ? [{ field: 'end_date', description: 'end_date must be YYYY-MM-DD' }]\n      : []),\n    ...(req.startDate && req.endDate\n      && startDateValid && endDateValid\n      && req.startDate > req.endDate\n      ? [{ field: 'start_date', description: 'start_date must not be after end_date' }]\n      : []),\n  ];\n  if (violations.length > 0) throw new ValidationError(violations);\n\n  const limit = req.limit > 0 ? Math.min(req.limit, MAX_LIMIT) : DEFAULT_LIMIT;\n\n  const result = await searchEdgarFullText({\n    query,\n    forms: formsNormalized || undefined,\n    startDate: req.startDate,\n    endDate: req.endDate,\n    size: limit,\n  });\n\n  if (!result) {\n    return { results: [], total: 0, unavailable: true, fetchedAtMs: Date.now() };\n  }\n\n  return {\n    results: result.results.slice(0, limit),\n    total: result.total,","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/server/worldmonitor/intelligence/v1/search-sec-filings.ts#L31-L67","documentation":"Cross-field ValidationError: when startDate and endDate are both individually valid ISO dates, the handler compares them lexicographically (req.startDate > req.endDate, which equals chronological order for strict YYYY-MM-DD strings) and rejects an inverted range. The violation is reported on the start_date field. Failing closed here prevents an inverted range from being sent to efts.sec.gov, where it would match nothing while the caller assumes a valid window.","triggerScenarios":"Calling searchSecFilings with startDate=\"2024-12-31\", endDate=\"2024-01-01\" (reversed arguments); month/day transposition when hand-typing dates (\"2024-05-03\" vs \"2024-03-05\"); or computing endDate from local time in a timezone behind UTC while startDate comes from a UTC source, so end lands before start.","commonSituations":"A 'date range' UI that lets users pick the start after the end without enforcing order; parameter order confusion between (from, to) and (to, from); timezone drift near UTC midnight when deriving 'today' on the client.","solutions":["Swap or sort the pair before the call: const [s, e] = [start, end].sort()","Validate the range client-side with the same comparison before invoking the RPC","Constrain the date picker so end >= start is unselectable"],"exampleFix":"// before\nawait client.searchSecFilings({ query, startDate: endPicker, endDate: startPicker }); // swapped -> throws\n\n// after\nconst [startDate, endDate] = [startPicker, endPicker].sort();\nawait client.searchSecFilings({ query, startDate, endDate });","handlingStrategy":"validation","validationCode":"if (req.startDate && req.endDate) {\n  if (!isEdgarIsoDate(req.startDate) || !isEdgarIsoDate(req.endDate)) throw new Error('date format');\n  if (req.startDate > req.endDate) {\n    [req.startDate, req.endDate] = [req.endDate, req.startDate]; // or reject in the UI\n  }\n}\nawait client.searchSecFilings(req);","typeGuard":"function isValidDateRange(s: string, e: string): s is string {\n  return isEdgarIsoDate(s) && isEdgarIsoDate(e) && s <= e;\n}","tryCatchPattern":"try { await client.searchSecFilings(req); }\ncatch (e) {\n  if (e instanceof ValidationError && e.violations?.some(v => v.description.includes('must not be after'))) {\n    showRangeError('Start date must not be after end date');\n  } else throw e;\n}","preventionTips":["Constrain the date picker so an inverted range cannot be selected","Derive both dates from one clock/timezone source","Sort the pair defensively right before the call"],"tags":["sec-edgar","date-range","validation"],"backgroundTag":"date-range-validation","analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}