koala73/worldmonitor · warning · Error
relay returned ${resp.status}
Error message
relay returned ${resp.status} What it means
Inside the cachedFetchJson producer of searchGoogleFlights, a non-OK status from the relay (/google-flights/search, 20s timeout) throws this Error. The handler's outer catch maps it to a degraded response {flights: [], degraded: true, error: 'relay returned <status>'}, so clients see a 200 with degraded=true; the failure is never cached, so subsequent calls retry the relay.
Source
Thrown at server/worldmonitor/aviation/v1/search-google-flights.ts:62
for (const airline of airlines) {
params.append('airlines', airline);
}
// Cache key uses a sorted-airlines axis so input order doesn't fragment cache hits;
// the relay still receives airlines in the caller's order via `params`.
const sortedAirlinesKey = [...airlines].sort().join(',');
const cacheKey = `aviation:gf:${origin}:${destination}:${departureDate}:${req.returnDate ?? ''}:${req.cabinClass ?? ''}:${req.maxStops ?? ''}:${req.departureWindow ?? ''}:${req.sortBy ?? ''}:${passengers}:${sortedAirlinesKey}:v1`;
try {
const data = await cachedFetchJson<{ flights: unknown[] }>(
cacheKey,
CACHE_TTL,
async () => {
const resp = await fetch(`${relayBaseUrl}/google-flights/search?${params}`, {
headers: getRelayHeaders(),
signal: AbortSignal.timeout(20_000),
});
if (!resp.ok) throw new Error(`relay returned ${resp.status}`);
const json = (await resp.json()) as { flights?: unknown[]; error?: string };
if (!Array.isArray(json.flights)) throw new Error(json.error ?? 'no results');
return { flights: json.flights };
},
);
if (!data) {
return { flights: [], degraded: true, error: 'no results' };
}
return {
flights: data.flights as SearchGoogleFlightsResponse['flights'],
degraded: false,
error: '',
};
} catch (err) {
return { flights: [], degraded: true, error: err instanceof Error ? err.message : 'search failed' };
}View on GitHub (pinned to 9361220cc0)
Solutions
- Branch on the degraded flag and error string instead of reading flights.length directly
- Confirm relay base URL and headers config on the calling service
- Correlate the status code in the error string with relay logs (401 secret vs 429 throttle vs 5xx health)
- Retry with backoff — cache is bypassed on failure, so the next attempt hits the relay again
Example fix
// before
const { flights } = await client.searchGoogleFlights(req);
return flights;
// after — propagate degradation instead of an empty list
const res = await client.searchGoogleFlights(req);
if (res.degraded) throw new UpstreamDegradedError(res.error);
return res.flights; Defensive patterns
Strategy: fallback
Type guard
interface FlightsResult { flights: unknown[]; degraded: boolean; error: string }
function isDegradedFlights(r: FlightsResult): boolean {
return r.degraded === true;
} Try / catch
const res = await searchGoogleFlights(req);
if (res.degraded && res.error.startsWith('relay returned')) {
return backoffAndRetry(req); // upstream status in the error string; not cached, safe to retry
} Prevention
- Check degraded before reading flights — an empty array means two different things here
- Correlate the numeric status in the error string with relay logs when triaging
- Give fare-search UIs an explicit 'data unavailable, retry' state distinct from 'no flights found'
When it happens
Trigger: Relay down/restarting (502/503); auth headers from getRelayHeaders() rejected (401 — rotated relay secret); relay throttling (429); WAF blocking the request; relay returning 500 on scraper exceptions.
Common situations: Relay environment variables stale after a secret rotation; relay host saturated by concurrent fare searches; relay deployed behind protective infrastructure that filters the edge's requests.
Related errors
- relay returned ${resp.status}
- no results
- no results
- DNS ${recordType} lookup failed: HTTP ${response.status}
- callbackUrl DNS resolution failed: ${message}
AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-08-21).
Data as JSON: /api/errors/264d0674677500bf.
Report an issue: GitHub.