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 storedView on GitHub (pinned to ffec79ac33)
Solutions
- 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.
- Retry with exponential backoff for 429 and 5xx (transient provider issues).
- Try a different country_code (ideally a major maritime nation) to determine if the failure is country-specific or systemic.
- 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
- Prefer coastal/maritime nations for vessel data — landlocked countries may return empty or error.
- Implement exponential backoff for 429/5xx — the maritime AIS provider may be rate-limited.
- Keep auth context fresh to avoid 401/403 failures.
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
- get-country-intel-brief HTTP ${res.status}${code ? `: ${code
- get-china-decision-signals returned no canonical payload
- Airspace data unavailable: both civilian and military source
- HTTP %d: %s
- serverUrl host is not allowed
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/219df6c159e9e7e6.
Report an issue: GitHub.