koala73/worldmonitor · error · ApiError

Airport delay page_size must be 0

Error message

Airport delay page_size must be 0

What it means

This endpoint returns the full seed-only airport delay listing and intentionally disallows pagination: page_size must be the literal string '0'. Any other value triggers ApiError 400 so clients cannot request paged results the backing seed cache cannot provide.

Solutions

  1. Omit page_size entirely from the request URL
  2. If page_size must be sent, send the exact value '0'
  3. Remove client-side pagination logic and consume the full response
  4. Check for an SDK/config default that injects page_size and disable it

Example fix

// before
const params = new URLSearchParams({ rpc: 'list-airport-delays', page_size: '25' });
// after
const params = new URLSearchParams({ rpc: 'list-airport-delays', page_size: '0' });
Defensive patterns

Strategy: validation

Validate before calling

if (params.has('page_size') && params.get('page_size') !== '0') throw new Error('page_size must be 0');

Type guard

const isPagedRequest = (p) => 'page_size' in p && String(p.page_size) !== '0';

Try / catch

try { await listAirportDelays(ctx, req); } catch (e) { if (e instanceof ApiError && e.status === 400 && e.message.includes('page_size must be 0')) { retryWithPageSizeZero(); } else throw e; }

Prevention

When it happens

Trigger: GET .../list-airport-delays?page_size=0&rpc=... works; ?page_size=25, ?page_size=10, or any non-'0' string throws.

Common situations: Generic API clients that always send a page_size; copied code from other list endpoints that support pagination; SDK defaults injecting page_size.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-09-22). Data as JSON: /api/errors/027d7e329a8531cf. Report an issue: GitHub.

Appendix: source

Thrown at server/worldmonitor/aviation/v1/list-airport-delays.ts:46

const FAA_CACHE_KEY = 'aviation:delays:faa:v1';
const INTL_CACHE_KEY = 'aviation:delays:intl:v3';

const FAA_AIRPORT_SET = new Set(FAA_AIRPORTS);
const INTL_AIRPORT_SET = new Set(AVIATIONSTACK_AIRPORTS);

const ALLOWED_QUERY_PARAMS = new Set(['page_size', 'cursor', 'region', 'min_severity', 'jmespath', '_debug', 'rpc']);

export async function listAirportDelays(
  ctx: ServerContext,
  req: ListAirportDelaysRequest,
): Promise<ListAirportDelaysResponse> {
  const seenParams = new Set<string>();
  for (const [key, value] of new URL(ctx.request.url).searchParams) {
    if (!ALLOWED_QUERY_PARAMS.has(key)) throw new ApiError(400, `Unsupported airport delay parameter: ${key}`, '');
    if (seenParams.has(key)) throw new ApiError(400, `Duplicate airport delay parameter: ${key}`, '');
    seenParams.add(key);
    if (key === 'page_size' && value !== '0') throw new ApiError(400, 'Airport delay page_size must be 0', '');
    if (key === 'rpc' && value !== 'list-airport-delays') throw new ApiError(400, 'Invalid airport delay route', '');
  }
  if ((req.pageSize ?? 0) !== 0 || req.cursor
    || (req.region && req.region !== 'AIRPORT_REGION_UNSPECIFIED')
    || (req.minSeverity && req.minSeverity !== 'FLIGHT_DELAY_SEVERITY_UNSPECIFIED')) {
    throw new ApiError(400, 'Airport delay filters are not supported', '');
  }
  // 1. FAA (US) — seed-only read
  // faaSourceCovered = the seed cache hit AND returned a valid alerts array.
  // A miss/parse-error means we have no telemetry for any FAA airport this
  // tick — we MUST NOT publish synthetic "normal" rows for them. See #3707.
  // PERF: the three inputs below are independent (different Redis keys / an
  // independent fetcher) and merge only afterwards — start them concurrently
  // instead of paying three serial round-trips per request.
  const faaRead = (async (): Promise<{ faaAlerts: AirportDelayAlert[]; faaSourceCovered: boolean; available: boolean }> => {
    let faaAlerts: AirportDelayAlert[] = [];
    let faaSourceCovered = false;
    try {

View on GitHub (pinned to e586b8b4b8)