koala73/worldmonitor · error

Imagery search failed: ${resp.status}

Error message

Imagery search failed: ${resp.status}

What it means

fetchImageryScenes() throws this when the imagery search API returns any non-OK response. The status code is embedded in the message; a 429 additionally arms the module-level cooldown before throwing. It surfaces upstream/API failures (bad request, auth, 5xx) rather than parsing errors.

Solutions

  1. Read the status in the message: fix a 400 by validating bbox (minLon<maxLon, lat within ±90) and datetime format before calling
  2. For 429, back off and retry after the cooldown (see the paired 'rate limited' error)
  3. For 5xx, retry with exponential backoff; check the API/server health if it persists
  4. Capture the response body for detail — the thrown message only carries the numeric status

Example fix

// before
if (!Number.isFinite(bbox[0]) || bbox[0] > bbox[2]) return;
// after
const [minLon, minLat, maxLon, maxLat] = bbox;
if ([minLon, minLat, maxLon, maxLat].some(Number.isNaN)) throw new Error('bbox must be 4 numbers');
if (minLon >= maxLon || minLat >= maxLat) throw new Error('bbox min must be < max');
Defensive patterns

Strategy: validation

Validate before calling

function isValidBbox(b: number[]): boolean { return b.length === 4 && b.every(Number.isFinite) && b[0] < b[2] && b[1] < b[3]; }

Type guard

const isImageryParams = (p: unknown): p is ImagerySearchParams => typeof p === 'object' && p !== null && Array.isArray((p as any).bbox) && isValidBbox((p as any).bbox);

Try / catch

try { scenes = await fetchImageryScenes(p); } catch (e) { const m = /Imagery search failed: (\d+)/.exec(e.message); if (m) logImageryHttpFailure(Number(m[1])); throw e; }

Prevention

When it happens

Trigger: Calling fetchImageryScenes() and receiving a non-2xx from /api/imagery/v1/search-imagery — e.g. 400 for a malformed bbox/datetime, 401/403 for auth, 429 for rate limiting, or 5xx from the upstream provider.

Common situations: Passing an invalid bbox (inverted or out-of-range coordinates); datetime strings the API rejects; server-side outage or upstream imagery provider error; hitting the API rate limit (429).

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/664b6904acaa70e6. Report an issue: GitHub.

Appendix: source

Thrown at src/services/imagery.ts:31

let retryAfterUntil = 0;

export async function fetchImageryScenes(params: ImagerySearchParams): Promise<ImageryScene[]> {
  if (Date.now() < retryAfterUntil) throw new Error('Imagery search rate limited');
  const url = new URL(toApiUrl('/api/imagery/v1/search-imagery'), window.location.origin);
  url.searchParams.set('bbox', params.bbox);
  if (params.datetime) url.searchParams.set('datetime', params.datetime);
  if (params.source) url.searchParams.set('source', params.source);
  if (params.limit) url.searchParams.set('limit', String(params.limit));

  const resp = await fetch(url.toString(), { signal: AbortSignal.timeout(15_000) });
  if (!resp.ok) {
    if (resp.status === 429) {
      const retryAfter = resp.headers.get('Retry-After');
      const seconds = retryAfter === null ? NaN : Number(retryAfter);
      const deadline = Number.isFinite(seconds) ? Date.now() + Math.max(0, seconds) * 1000 : Date.parse(retryAfter ?? '');
      retryAfterUntil = Number.isFinite(deadline) ? deadline : Date.now() + 60_000;
    }
    throw new Error(`Imagery search failed: ${resp.status}`);
  }
  const data = await resp.json();
  return data.scenes ?? [];
}

View on GitHub (pinned to 7d06c8633d)