koala73/worldmonitor · error · ValidationError

start_date must not be after end_date

Error message

start_date must not be after end_date

What it means

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.

Source

Thrown at server/worldmonitor/intelligence/v1/search-sec-filings.ts:49

  const startDateValid = !req.startDate || isEdgarIsoDate(req.startDate);
  const endDateValid = !req.endDate || isEdgarIsoDate(req.endDate);
  const violations = [
    ...(formsNormalized === null
      ? [{ field: 'forms', description: 'forms must be a comma-separated form list such as "8-K" or "10-K,10-Q"' }]
      : []),
    ...(!startDateValid
      ? [{ field: 'start_date', description: 'start_date must be YYYY-MM-DD' }]
      : []),
    ...(!endDateValid
      ? [{ field: 'end_date', description: 'end_date must be YYYY-MM-DD' }]
      : []),
    ...(req.startDate && req.endDate
      && startDateValid && endDateValid
      && req.startDate > req.endDate
      ? [{ field: 'start_date', description: 'start_date must not be after end_date' }]
      : []),
  ];
  if (violations.length > 0) throw new ValidationError(violations);

  const limit = req.limit > 0 ? Math.min(req.limit, MAX_LIMIT) : DEFAULT_LIMIT;

  const result = await searchEdgarFullText({
    query,
    forms: formsNormalized || undefined,
    startDate: req.startDate,
    endDate: req.endDate,
    size: limit,
  });

  if (!result) {
    return { results: [], total: 0, unavailable: true, fetchedAtMs: Date.now() };
  }

  return {
    results: result.results.slice(0, limit),
    total: result.total,

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Swap or sort the pair before the call: const [s, e] = [start, end].sort()
  2. Validate the range client-side with the same comparison before invoking the RPC
  3. Constrain the date picker so end >= start is unselectable

Example fix

// before
await client.searchSecFilings({ query, startDate: endPicker, endDate: startPicker }); // swapped -> throws

// after
const [startDate, endDate] = [startPicker, endPicker].sort();
await client.searchSecFilings({ query, startDate, endDate });
Defensive patterns

Strategy: validation

Validate before calling

if (req.startDate && req.endDate) {
  if (!isEdgarIsoDate(req.startDate) || !isEdgarIsoDate(req.endDate)) throw new Error('date format');
  if (req.startDate > req.endDate) {
    [req.startDate, req.endDate] = [req.endDate, req.startDate]; // or reject in the UI
  }
}
await client.searchSecFilings(req);

Type guard

function isValidDateRange(s: string, e: string): s is string {
  return isEdgarIsoDate(s) && isEdgarIsoDate(e) && s <= e;
}

Try / catch

try { await client.searchSecFilings(req); }
catch (e) {
  if (e instanceof ValidationError && e.violations?.some(v => v.description.includes('must not be after'))) {
    showRangeError('Start date must not be after end date');
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21). Data as JSON: /api/errors/5925292054b34ebf. Report an issue: GitHub.