koala73/worldmonitor · error · Error

get-vessel-snapshot HTTP ${res.status}${detail ? ` — ${detai

Error message

get-vessel-snapshot HTTP ${res.status}${detail ? ` — ${detail}` : ''}

What it means

Thrown by the get_vessel_snapshot MCP tool when the downstream vessel endpoint returned a non-OK HTTP status. The code first checks for a billing denial (throwIfBillingDenial, re-thrown separately), then extracts up to 200 characters of error detail from the response body and includes it in the message. The 8-second fetch timeout means a slow upstream can also surface here indirectly.

Source

Thrown at api/mcp/registry/rpc-tools.ts:1478

      // nested `location` (the previous snake_case reads matched nothing, so
      // density_zones was permanently empty).
      type VesselLoc = { latitude?: number; longitude?: number };
      type VesselResp = {
        snapshot?: {
          snapshotAt?: number;
          densityZones?: { name?: string; location?: VesselLoc; intensity?: number; shipsPerDay?: number; deltaPct?: number; note?: string }[];
          disruptions?: { name?: string; type?: string; severity?: string; location?: VesselLoc; darkShips?: number; vesselCount?: number; region?: string; description?: string }[];
        };
      };

      const res = await fetch(url, {
        headers: { ...auth, 'User-Agent': 'worldmonitor-mcp-edge/1.0' },
        signal: AbortSignal.timeout(8_000),
      });
      if (!res.ok) {
        throwIfBillingDenial(res, 'get-vessel-snapshot');
        const detail = (await res.text().catch(() => '')).slice(0, 200);
        throw new Error(`get-vessel-snapshot HTTP ${res.status}${detail ? ` — ${detail}` : ''}`);
      }
      const data = await res.json() as VesselResp;
      const snap = data.snapshot ?? {};

      // 3° pad: maritime zones sit offshore, outside land bboxes (e.g. the
      // Strait of Hormuz at 26.6N/56.3E vs AE's ne corner at 26.06/56.38).
      // (0,0) is the handler's default for missing coordinates → exclude.
      const PAD_DEG = 3;
      const inCountryBbox = (loc?: VesselLoc): boolean => {
        const lat = loc?.latitude ?? 0;
        const lon = loc?.longitude ?? 0;
        if (lat === 0 && lon === 0) return false;
        if (lat < sw_lat - PAD_DEG || lat > ne_lat + PAD_DEG) return false;
        const lo = sw_lon - PAD_DEG;
        // Source boxes stored wrapped (sw_lon > ne_lon) span the dateline;
        // unwrap to a monotonic interval before reasoning about the pad.
        const hi = (sw_lon > ne_lon ? ne_lon + 360 : ne_lon) + PAD_DEG;
        // Pad widened the interval to the full circle — AQ and RU are stored

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Read the HTTP status from the error message: 429 — retry with backoff; 5xx — retry after delay; 400 — verify country_code; 401/403 — check auth context.
  2. Retry with exponential backoff for 429 and 5xx (transient provider issues).
  3. Try a different country_code (ideally a major maritime nation) to determine if the failure is country-specific or systemic.
  4. If the detail mentions a timeout, the upstream provider is slow — retry once; persistent timeouts indicate a provider degradation.
Defensive patterns

Strategy: retry

Validate before calling

// Validate country_code and auth before calling get_vessel_snapshot
if (!/^[A-Z]{2}$/.test(countryCode)) {
  throw new Error('country_code must be ISO 3166-1 alpha-2');
}
if (context.expiresAt && Date.now() > context.expiresAt) {
  throw new Error('Auth context expired');
}

Type guard

function isVesselSnapshotHttpError(e: unknown): boolean {
  return e instanceof Error && /get-vessel-snapshot HTTP \d+/.test(e.message);
}

Try / catch

try {
  const vessels = await callMcpTool('get_vessel_snapshot', { country_code: 'AE' });
} catch (e) {
  if (e instanceof Error) {
    const status = e.message.match(/HTTP (\d+)/)?.[1];
    if (status === '429' || (status && Number(status) >= 500)) {
      await exponentialBackoffRetry();
    } else if (status === '401' || status === '403') {
      refreshAuthContext();
    } else {
      throw e;
    }
  } else throw e;
}

Prevention

When it happens

Trigger: The vessel snapshot handler returned 4xx/5xx — common causes: 401/403 (auth/HMAC failure), 429 (rate limit from the maritime data provider), 400 (invalid country_code or bbox parameters), 500/502/504 (upstream provider outage — MarineTraffic/AIS source), or the 8s AbortSignal.timeout firing. Billing denial (402) is intercepted earlier and never reaches this throw.

Common situations: Maritime AIS provider outage or rate limit; an unsupported or non-coastal country_code with no vessel data (though this may return empty 200 rather than error); auth context expiry causing 401/403; a slow provider response exceeding the 8s budget.

Related errors


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/219df6c159e9e7e6. Report an issue: GitHub.