koala73/worldmonitor · error · ValidationError

end_date must be YYYY-MM-DD

Error message

end_date must be YYYY-MM-DD

What it means

Thrown by searchSecFilings when the endDate filter fails isEdgarIsoDate (server/_shared/sec-edgar.ts:492). The check is deliberately strict: the value must match /^\d{4}-\d{2}-\d{2}$/ AND round-trip through Date.UTC, so calendar-impossible dates like 2024-02-30 are rejected even though Date.parse would silently normalize them. The handler fails closed instead of dropping the malformed filter, because silently dropping a date filter would widen the EDGAR result set while the caller believes the range was applied.

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 zero-padded YYYY-MM-DD, e.g. d.toISOString().slice(0,10)
  2. Validate client-side with the same regex + calendar round-trip before calling the RPC
  3. Omit endDate entirely when no upper bound is needed
  4. If the user typed the date, parse and re-serialize it in the UI before submit

Example fix

// before
const resp = await client.searchSecFilings({ query: 'material cyber', endDate: new Date().toString() }); // throws

// after
const today = new Date().toISOString().slice(0, 10); // "2026-08-21"
const resp = await client.searchSecFilings({ query: 'material cyber', endDate: today });
Defensive patterns

Strategy: validation

Validate before calling

const EDGAR_ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
function isEdgarIsoDate(value: string): boolean {
  if (!EDGAR_ISO_DATE_RE.test(value)) return false;
  const y = Number(value.slice(0, 4));
  const m = Number(value.slice(5, 7));
  const d = Number(value.slice(8, 10));
  const dt = new Date(Date.UTC(y, m - 1, d));
  return dt.getUTCFullYear() === y && dt.getUTCMonth() === m - 1 && dt.getUTCDate() === d;
}
if (req.endDate !== undefined && !isEdgarIsoDate(req.endDate)) {
  throw new Error(`endDate must be YYYY-MM-DD, got: ${req.endDate}`);
}
await client.searchSecFilings(req);

Type guard

function isEdgarIsoDate(value: unknown): value is `${number}${number}${number}${number}-${number}${number}-${number}${number}` {
  return typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value)
    && isEdgarIsoDate(value); // shape + calendar round-trip
}

Try / catch

try { await client.searchSecFilings(req); }
catch (e) {
  if (e instanceof ValidationError) {
    const bad = e.violations?.filter(v => v.field === 'end_date');
    if (bad) markEndDateInvalid(bad.map(v => v.description).join('; '));
  } throw e;
}

Prevention

When it happens

Trigger: Calling the searchSecFilings RPC with endDate="01/31/2024" (US format), "2024-1-5" (unpadded), "20240131", "2024-01-31T00:00:00Z" (datetime instead of date-only), " 2024-01-31" (whitespace), or impossible dates like "2023-02-29" or "2024-13-01". Only leaving endDate unset/undefined skips the check.

Common situations: Building the date from a JS Date without zero-padding (getMonth() is 0-indexed, getDay() vs getDate() confusion), passing free-text user input straight through, or reusing an ISO 8601 datetime string from another API where a date-only string is required.

Related errors


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