koala73/worldmonitor · error · ValidationError

start_date must be YYYY-MM-DD

Error message

start_date must be YYYY-MM-DD

What it means

searchSecFilings includes this violation (field 'start_date') when req.startDate is present but fails isEdgarIsoDate — EDGAR full-text search accepts strict YYYY-MM-DD calendar dates only. The fail-closed design means a malformed date is reported instead of silently dropped, because dropping it would widen the date range and return filings the caller believes were excluded. It is thrown together with any other accumulated violations.

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. Format dates as YYYY-MM-DD before sending — e.g. date.toISOString().slice(0, 10)
  2. Validate with a strict /^\d{4}-\d{2}-\d{2}$/ check plus a real-calendar parse on the client
  3. Leave start_date unset when you only want an end-date-bounded search

Example fix

// before — full ISO timestamp sent
{ query: 'insider sales', startDate: fromDate.toISOString() }

// after — calendar date only
{ query: 'insider sales', startDate: fromDate.toISOString().slice(0, 10) }
Defensive patterns

Strategy: validation

Validate before calling

// strict EDGAR calendar-date check (shape + real calendar day)
function isEdgarIsoDate(s: string): boolean {
  if (!/^\d{4}-\d{2}-\d{2}$/.test(s)) return false;
  const d = new Date(`${s}T00:00:00Z`);
  return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === s;
}
const startDate = rawStart ? rawStart.toISOString().slice(0, 10) : undefined;

Type guard

function isStartDateViolation(body: unknown): boolean {
  const v = (body as { violations?: { field?: string }[] })?.violations;
  return Array.isArray(v) && v.some((x) => x.field === 'start_date');
}

Try / catch

try {
  await searchSecFilings({ query, startDate });
} catch (e) {
  if (e instanceof HttpError && e.status === 400 && isStartDateViolation(e.body)) {
    return reformatDatePicker(); // strict YYYY-MM-DD required; timestamps and slashes rejected
  }
  throw e;
}

Prevention

When it happens

Trigger: startDate='2024/01/01' (slashes); ISO datetime '2024-01-01T00:00:00Z'; compact '20240101'; locale-formatted '01-01-2024'; trailing whitespace or newline in the value.

Common situations: Date picked in a UI and serialized with the locale format; Date.toISOString() used directly (produces a full timestamp, not a date); API gateway re-encoding parameters; copy-pasted dates from spreadsheets.

Related errors


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