{"record":{"id":"6a82a1d15e8671c0","repo":"koala73/worldmonitor","slug":"invalid-imagery-datetime","errorCode":null,"errorMessage":"Invalid imagery datetime","messagePattern":"Invalid imagery datetime","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"warning","filePath":"server/worldmonitor/imagery/v1/search-imagery.ts","lineNumber":138,"sourceCode":"  _ctx: ServerContext,\n  req: SearchImageryRequest,\n): Promise<SearchImageryResponse> {\n  if (!req.bbox) {\n    return { scenes: [], totalResults: 0, cacheHit: false };\n  }\n\n  const parsedBbox = validateBbox(req.bbox);\n  if (!parsedBbox) {\n    return { scenes: [], totalResults: 0, cacheHit: false };\n  }\n\n  const limit = Math.max(1, Math.min(50, req.limit || 10));\n  const nowHour = new Date();\n  nowHour.setMinutes(0, 0, 0);\n  const weekAgo = new Date(nowHour.getTime() - 7 * 24 * 60 * 60 * 1000);\n  const defaultDatetime = `${weekAgo.toISOString().split('.')[0]}Z/${nowHour.toISOString().split('.')[0]}Z`;\n  const datetime = normalizeDatetime(req.datetime || defaultDatetime);\n  if (datetime === null) throw new ValidationError([{ field: 'datetime', description: 'Invalid imagery datetime' }]);\n  const source = (req.source ?? '').trim().toLowerCase();\n  const matchedCollections = COLLECTIONS.filter(collection => collection.includes(source));\n  const collections = matchedCollections.length > 0 ? matchedCollections : COLLECTIONS;\n  const body = JSON.stringify({\n    bbox: parsedBbox,\n    datetime,\n    collections,\n    limit,\n    sortby: [{ field: 'properties.datetime', direction: 'desc' }],\n  });\n\n  try {\n    const key = `imagery:search:v2:${await sha256Hex(body)}`;\n    const result = await cachedFetchJsonWithMeta<{ scenes: ImageryScene[]; totalResults: number }>(\n      key,\n      CACHE_TTL,\n      async () => {\n","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/server/worldmonitor/imagery/v1/search-imagery.ts#L120-L156","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait searchImagery(ctx, { datetime: '01/02/2026' });\n// after\nawait searchImagery(ctx, { datetime: '2026-01-01T00:00:00Z/2026-01-08T00:00:00Z' });","handlingStrategy":"validation","validationCode":"const iso = /^\\d{4}-\\d{2}-\\d{2}(T[\\d:.]+Z)?(\\/\\d{4}-\\d{2}-\\d{2}(T[\\d:.]+Z)?)?$/;\nif (req.datetime && !iso.test(req.datetime.trim())) throw new Error('datetime must be ISO-8601 instant or start/end range');","typeGuard":"const isIsoDatetime = (v: unknown): v is string =>\n  typeof v === 'string' && !Number.isNaN(Date.parse(v.includes('/') ? v.split('/')[0] : v));","tryCatchPattern":"try {\n  return await searchImagery(ctx, { datetime });\n} catch (e) {\n  if (e instanceof ValidationError && e.issues.some(i => i.field === 'datetime')) {\n    return await searchImagery(ctx, {}); // default last-7-days window\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["validation","date","imagery"],"backgroundTag":"invalid-date-format","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}