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
- Format dates as zero-padded YYYY-MM-DD, e.g. d.toISOString().slice(0,10)
- Validate client-side with the same regex + calendar round-trip before calling the RPC
- Omit endDate entirely when no upper bound is needed
- 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
- Always emit date filters with toISOString().slice(0,10)
- Never pass raw user text as startDate/endDate — parse and re-serialize in the UI
- Copy isEdgarIsoDate into a shared client util so both sides use identical rules
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
- start_date must be YYYY-MM-DD
- start_date must not be after end_date
- get_intel_timeline requires at least one of domain ("conflic
- INCOMPATIBLE_DELIVERY
- COUNTRIES_LIMIT_EXCEEDED
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/8b709a29b72282e0.
Report an issue: GitHub.