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
- Read the status in the message: fix a 400 by validating bbox (minLon<maxLon, lat within ±90) and datetime format before calling
- For 429, back off and retry after the cooldown (see the paired 'rate limited' error)
- For 5xx, retry with exponential backoff; check the API/server health if it persists
- 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
- Validate bbox and datetime (ISO 8601) before calling
- Back off on 429 and 5xx with exponential retry
- Monitor the imagery endpoint's health and statuses
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
- ${operation} HTTP ${status}: ${safeCode}
- Exa ${endpoint.slice(1)} failed HTTP ${response.status}: ${d
- Firecrawl scrape failed: HTTP ${resp.status}
- Firecrawl extract failed: HTTP ${resp.status}
- Firecrawl search failed: HTTP ${resp.status}
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)