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

  1. Verify client.mcp_url — it must be the MCP endpoint (default https://worldmonitor.app/mcp), not the REST API host.
  2. If self-hosting, confirm the relay accepts JSON POSTs to /mcp and returns JSON, not an HTML error page.
  3. For 502/503/504, retry with exponential backoff — these are transient gateway failures.
  4. 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

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


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