koala73/worldmonitor · warning · ValidationError
Invalid imagery datetime
Error message
Invalid imagery datetime
What it means
searchImagery normalizes req.datetime (or a default last-7-days window) through normalizeDatetime(); if the value cannot be parsed into a valid STAC-style datetime or range, it throws ValidationError on field 'datetime' with 'Invalid imagery datetime'.
Solutions
- Send an ISO-8601 instant ('2026-01-01T00:00:00Z') or a start/end range joined with '/'
- If you want recent imagery, omit datetime entirely to use the default last-7-days window
- Catch ValidationError and map the field 'datetime' back to the date input for correction
Example fix
// before
await searchImagery(ctx, { datetime: '01/02/2026' });
// after
await searchImagery(ctx, { datetime: '2026-01-01T00:00:00Z/2026-01-08T00:00:00Z' }); Defensive patterns
Strategy: validation
Validate before calling
const iso = /^\d{4}-\d{2}-\d{2}(T[\d:.]+Z)?(\/\d{4}-\d{2}-\d{2}(T[\d:.]+Z)?)?$/;
if (req.datetime && !iso.test(req.datetime.trim())) throw new Error('datetime must be ISO-8601 instant or start/end range'); Type guard
const isIsoDatetime = (v: unknown): v is string =>
typeof v === 'string' && !Number.isNaN(Date.parse(v.includes('/') ? v.split('/')[0] : v)); Try / catch
try {
return await searchImagery(ctx, { datetime });
} catch (e) {
if (e instanceof ValidationError && e.issues.some(i => i.field === 'datetime')) {
return await searchImagery(ctx, {}); // default last-7-days window
}
throw e;
} Prevention
- Generate datetimes with toISOString(), not locale date formatting
- Use start/end ranges joined with '/' for windows
- Omit datetime to accept the default recent window
- Validate start <= end before sending ranges
When it happens
Trigger: datetime values like '2024-13-45', 'last week', '2024/01/01' (wrong separator), a reversed range where start > end, or open ranges in a form normalizeDatetime does not accept.
Common situations: Free-text date inputs passed straight through; date components reordered per locale (DD/MM vs MM/DD); timestamps with local timezone offsets that fail the expected ISO-8601 shape; forgetting the '/'-separated range format.
Related errors
- Use a valid date from 1900 through 9998.
- ${label} HTTP 400
- Could not resolve ${JSON.stringify(echoCountryInput(raw))} t
- INCOMPATIBLE_DELIVERY
- COUNTRIES_LIMIT_EXCEEDED
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/6a82a1d15e8671c0.
Report an issue: GitHub.
Appendix: source
Thrown at server/worldmonitor/imagery/v1/search-imagery.ts:138
_ctx: ServerContext,
req: SearchImageryRequest,
): Promise<SearchImageryResponse> {
if (!req.bbox) {
return { scenes: [], totalResults: 0, cacheHit: false };
}
const parsedBbox = validateBbox(req.bbox);
if (!parsedBbox) {
return { scenes: [], totalResults: 0, cacheHit: false };
}
const limit = Math.max(1, Math.min(50, req.limit || 10));
const nowHour = new Date();
nowHour.setMinutes(0, 0, 0);
const weekAgo = new Date(nowHour.getTime() - 7 * 24 * 60 * 60 * 1000);
const defaultDatetime = `${weekAgo.toISOString().split('.')[0]}Z/${nowHour.toISOString().split('.')[0]}Z`;
const datetime = normalizeDatetime(req.datetime || defaultDatetime);
if (datetime === null) throw new ValidationError([{ field: 'datetime', description: 'Invalid imagery datetime' }]);
const source = (req.source ?? '').trim().toLowerCase();
const matchedCollections = COLLECTIONS.filter(collection => collection.includes(source));
const collections = matchedCollections.length > 0 ? matchedCollections : COLLECTIONS;
const body = JSON.stringify({
bbox: parsedBbox,
datetime,
collections,
limit,
sortby: [{ field: 'properties.datetime', direction: 'desc' }],
});
try {
const key = `imagery:search:v2:${await sha256Hex(body)}`;
const result = await cachedFetchJsonWithMeta<{ scenes: ImageryScene[]; totalResults: number }>(
key,
CACHE_TTL,
async () => {
View on GitHub (pinned to 7d06c8633d)