koala73/worldmonitor · error · Error
HTTP ${resp.status}
Error message
HTTP ${resp.status} What it means
Thrown by McpConnectModal's connect flow when the GET to /api/mcp-proxy?serverUrl=... (via premiumFetch, 20s AbortSignal.timeout) fails: the error message is data.error from the proxy's JSON body if present (e.g. the remote MCP server was unreachable, the SSE/StreamableHTTP handshake failed, or the server returned a protocol error), otherwise the literal 'HTTP <status>'. The catch shows it next to 'Connect failed'.
Source
Thrown at src/components/McpConnectModal.ts:417
const serverUrl = urlInput.value.trim();
if (!serverUrl) return;
track('mcp-connect-attempt');
connectStatus.textContent = t('mcp.connecting');
connectStatus.className = 'mcp-connect-status mcp-status-loading';
connectBtn.disabled = true;
try {
const headers = getEffectiveHeaders();
const qs = new URLSearchParams({ serverUrl });
if (Object.keys(headers).length) qs.set('headers', JSON.stringify(headers));
// premiumFetch attaches the Clerk Pro Bearer for normal web Pro
// users. /api/mcp-proxy is in PREMIUM_RPC_PATHS so the path gate
// fires; the server-side isCallerPremium check accepts Bearer,
// wm_ user keys, and enterprise keys (PR #3768).
const resp = await premiumFetch(`${proxyUrl('/api/mcp-proxy')}?${qs}`, {
signal: AbortSignal.timeout(20_000),
});
const data = await resp.json() as { tools?: McpToolDef[]; error?: string };
if (!resp.ok || data.error) throw new Error(data.error || `HTTP ${resp.status}`);
tools = data.tools ?? [];
connectStatus.textContent = t('mcp.foundTools', { count: String(tools.length) });
connectStatus.className = 'mcp-connect-status mcp-status-ok';
track('mcp-connect-success', { toolCount: tools.length });
toolsSection.style.display = '';
renderTools(tools);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
connectStatus.textContent = `${t('mcp.connectFailed')}: ${msg}`;
connectStatus.className = 'mcp-connect-status mcp-status-error';
} finally {
connectBtn.disabled = false;
}
});
addBtn.addEventListener('click', () => {
if (!selectedTool) return;
track('mcp-panel-add', { tool: selectedTool.name });View on GitHub (pinned to eeab0a219f)
Solutions
- Verify the MCP server URL opens and negotiates in a standalone MCP client first; if connecting to localhost, run the browser page locally too
- Check the user's Pro auth (Bearer/wm_ key per PR #3768) — a 401 here is the premium gate, not the remote server
- Use the proxy's data.error text: it names the remote failure reason; correlate with server-side mcp-proxy logs
- Slow server? The 20s AbortSignal.timeout in the connect call is the ceiling — start the server before connecting or raise the budget
Example fix
// before
const resp = await premiumFetch(`${proxyUrl('/api/mcp-proxy')}?${qs}`, { signal: AbortSignal.timeout(20_000) });
if (!resp.ok || data.error) throw new Error(data.error || `HTTP ${resp.status}`);
// after (pre-validate the URL shape before hitting the proxy):
const parsed = new URL(serverUrl); // throws on malformed input before any network call
if (!/^https?:$/.test(parsed.protocol)) throw new Error('serverUrl must be http(s)');
const resp = await premiumFetch(`${proxyUrl('/api/mcp-proxy')}?${qs}`, { signal: AbortSignal.timeout(20_000) });
if (!resp.ok || data.error) throw new Error(data.error || `HTTP ${resp.status}`); Defensive patterns
Strategy: try-catch
Validate before calling
let parsed: URL;
try { parsed = new URL(serverUrl); } catch { return connectFailed('serverUrl is not a valid URL'); }
if (!/^https?:$/.test(parsed.protocol)) return connectFailed('serverUrl must be http(s)'); Type guard
function isHttpUrl(value: string): boolean { try { return /^https?:$/.test(new URL(value).protocol); } catch { return false; } } Try / catch
catch (err) { const msg = err instanceof Error ? err.message : String(err); if (/^HTTP \d+$/.test(msg)) { const s = Number(msg.slice(5)); if (s === 401 || s === 403) showUpgradeAuth(); else showProxyIssue(s); } else showRemoteServerError(msg); // data.error path names the remote cause } Prevention
- Verify the MCP server URL in a standalone MCP client before wiring it into the dashboard
- Remember localhost URLs only work when the page itself runs locally
- Read data.error first — it carries the remote server's own failure reason; correlate with proxy logs
When it happens
Trigger: Connecting to an MCP server URL that is down, not an MCP endpoint, requires auth not supplied in the custom headers, or speaks a protocol the proxy cannot negotiate (data.error path); a 401/403 from the proxy's isCallerPremium gate (web user without Pro, or a wm_/enterprise key the server rejects); 404/5xx from the proxy itself (HTTP path). Note: exceeding the 20s timeout throws an AbortError with a TimeoutError cause, not this message.
Common situations: Typo'd or stale serverUrl (server moved); local MCP server not running when connecting from the hosted app (localhost URL from a deployed page); Pro subscription lapsed so the path gate 401s; corporate proxy stripping SSE so the handshake errors.
Related errors
- HTTP ${resp.status}
- HTTP ${res.status}
- DNS ${recordType} lookup failed: status ${data.Status}
- HTTP ${resp.status}
- Brief service unavailable (${res.status})
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/b3925b3e5a2cd690.
Report an issue: GitHub.