koala73/worldmonitor · error · APIError
HTTP %d: %s
Error message
HTTP %d: %s
What it means
Raised as APIError by _rpc() (line 290-291) when the MCP transport — a POST to mcp_url — returns a non-2xx HTTP status AND the parsed body was not a JSON-RPC error object (a JSON-RPC error would have raised MCPError first at line 287-289). This is a transport/gateway-level failure of the MCP endpoint itself, not an application-level JSON-RPC error. The body is whatever the server/proxy returned (often HTML), truncated to 300 chars.
Source
Thrown at sdk/python/src/worldmonitor_sdk/__init__.py:291
headers = self._headers(accept="application/json, text/event-stream")
headers["content-type"] = "application/json"
status, content_type, text = self._transport(
{
"url": self.mcp_url,
"method": "POST",
"headers": headers,
"body": json.dumps(rpc).encode("utf-8"),
},
self.timeout,
)
value = parse_body(text, content_type)
# A JSON-RPC error object wins over the HTTP status (the server pairs
# auth errors with a 200 on some transports).
if isinstance(value, dict) and isinstance(value.get("error"), dict):
err = value["error"]
raise MCPError(err.get("code", 0), err.get("message", ""), err.get("data"))
if status < 200 or status >= 300:
raise APIError(status, value)
if isinstance(value, dict) and "result" in value:
return value["result"]
return value
def _stringify(value):
if value is True:
return "true"
if value is False:
return "false"
return str(value)
__all__ = [
"API_KEY_HEADER",
"AUTH_HINT",
"APIError",
"Client",View on GitHub (pinned to ffec79ac33)
Solutions
- Verify client.mcp_url — it must be the MCP endpoint (default https://worldmonitor.app/mcp), not the REST API host.
- If self-hosting, confirm the relay accepts JSON POSTs to /mcp and returns JSON, not an HTML error page.
- For 502/503/504, retry with exponential backoff — these are transient gateway failures.
- If a WAF/CDN is blocking, ensure the User-Agent header (set automatically by the SDK) is preserved end-to-end.
Example fix
# before client = Client(mcp_url='https://api.worldmonitor.app/mcp') # wrong host # after client = Client() # DEFAULT_MCP_URL = https://worldmonitor.app/mcp
Defensive patterns
Strategy: retry
Validate before calling
# Confirm the MCP endpoint accepts a POST before relying on it in a hot path.
import urllib.request, json
req = urllib.request.Request(client.mcp_url, data=json.dumps({'jsonrpc':'2.0','id':1,'method':'tools/list'}).encode(),
headers={'content-type':'application/json','accept':'application/json'}, method='POST')
try:
with urllib.request.urlopen(req, timeout=10) as r:
assert 200 <= r.status < 300, f'MCP endpoint returned {r.status}'
except Exception as e:
raise SystemExit(f'MCP transport unreachable at {client.mcp_url}: {e}') Type guard
from worldmonitor_sdk import APIError
def is_mcp_transport_error(exc: Exception) -> bool:
# APIError raised from _rpc (MCP path) rather than get (REST path) is not
# distinguishable by class alone — distinguish by call site in the handler.
return isinstance(exc, APIError) and exc.status in (403, 404, 405, 500, 502, 503, 504) Try / catch
import time, random
from worldmonitor_sdk import APIError, MCPError, WorldMonitorError
for attempt in range(4):
try:
result = client.call_tool('get_market_data', asset_class='crypto')
break
except MCPError:
raise # JSON-RPC application error — not a transport issue
except APIError as e:
if e.status in (502, 503, 504) and attempt < 3:
time.sleep((2 ** attempt) + random.random())
continue
raise
else:
raise RuntimeError('MCP transport failed after retries') Prevention
- Leave mcp_url on the default production endpoint unless you operate a self-hosted relay.
- Do not set WORLDMONITOR_MCP_URL to the REST base_url — the MCP endpoint is a distinct host/path.
- Ensure any reverse proxy in front of a self-hosted /mcp passes POST bodies through unchanged.
- Retry only transient gateway statuses (502/503/504); a persistent 404 means the route is missing.
When it happens
Trigger: WORLDMONITOR_MCP_URL overridden to a host that does not accept POST or has no /mcp route (404, 405). A reverse proxy or Cloudflare WAF returns an HTML challenge/block page with a 403/503 status (non-JSON, so no JSON-RPC error object to match). The MCP endpoint is down or returning 502/504 from an upstream gateway. mcp_url accidentally set to the REST base_url (https://api.worldmonitor.app instead of https://worldmonitor.app/mcp).
Common situations: Self-hosted relay without the /mcp route mounted. Corporate egress proxy that strips or rewrites the POST. A DNS or TLS failure that surfaces as a proxy 502. Mismatched mcp_url and base_url after an environment migration.
Related errors
- HTTP %d: %s%s
- -32001
- get-china-decision-signals returned no canonical payload
- get-country-intel-brief HTTP ${res.status}${code ? `: ${code
- get-vessel-snapshot HTTP ${res.status}${detail ? ` — ${detai
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/bd0960b35559d903.
Report an issue: GitHub.