koala73/worldmonitor · error · Error

HTTP ${resp.status}

Error message

HTTP ${resp.status}

What it means

Thrown by McpDataPanel when invoking a remote MCP tool via POST /api/mcp-proxy fails: the JSON body carries { serverUrl, toolName, toolArgs, customHeaders }, and on failure the message is data.error from the proxy response (remote tool error, unknown tool, argument validation failure at the MCP server) or the literal 'HTTP <status>' when the body carries no error field. The signal is AbortSignal.any([destroyController, timeout(20s)]), so panel destruction aborts instead of throwing this.

Source

Thrown at src/components/McpDataPanel.ts:118

    this.showLoading();
    try {
      // 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'), {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          serverUrl: this.spec.serverUrl,
          toolName: this.spec.toolName,
          toolArgs: this.spec.toolArgs,
          customHeaders: this.spec.customHeaders,
        }),
        signal: AbortSignal.any([this.destroyController.signal, AbortSignal.timeout(20_000)]),
      });
      const data = await resp.json() as { result?: McpResult; error?: string };
      if (!resp.ok || data.error) throw new Error(data.error || `HTTP ${resp.status}`);
      this.lastFetchedAt = Date.now();
      this.renderResult(data.result ?? {});
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      this.showError(msg);
    }
  }

  private renderResult(result: McpResult): void {
    const jsonData = this.extractJsonData(result);

    if (jsonData !== null && isProWidgetEnabled()) {
      const hash = JSON.stringify(jsonData).slice(0, 8192);
      if (hash === this.lastJsonHash && this.cachedWidgetHtml) {
        this.setSafeContent(unsafeRawHtml(`
          <div class="mcp-panel-meta">${this.buildMetaLine()}</div>
          <div class="mcp-panel-content mcp-panel-widget">${wrapProWidgetHtml(this.cachedWidgetHtml)}</div>
        `, 'legacy Panel.setContent() migration'));

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Validate toolName and toolArgs against the tools list returned at connect time before invoking; re-connect to refresh it
  2. Read data.error first — it is the remote MCP server's own error text and names the real cause (unknown tool vs auth vs crash)
  3. Re-run connect to confirm the server is still reachable and the user's premium auth still passes the proxy gate
  4. Persist server credentials with the spec so customHeaders survive reloads instead of silently degrading to 401

Example fix

// before
const data = await resp.json();
if (!resp.ok || data.error) throw new Error(data.error || `HTTP ${resp.status}`);

// after (gate the call on the connected tool inventory):
if (!this.knownTools?.some(t => t.name === this.spec.toolName)) {
  this.showError(`Unknown tool: ${this.spec.toolName}`); // fail before the network round-trip
  return;
}
const data = await resp.json();
if (!resp.ok || data.error) throw new Error(data.error || `HTTP ${resp.status}`);
Defensive patterns

Strategy: try-catch

Validate before calling

const known = this.knownTools?.map(t => t.name) ?? [];
if (!known.includes(this.spec.toolName)) { this.showError(`Unknown tool: ${this.spec.toolName}`); return; }

Type guard

function isKnownTool(spec: { toolName: string }, known: { name: string }[]): boolean { return known.some(t => t.name === spec.toolName); }

Try / catch

catch (err) { const msg = err instanceof Error ? err.message : String(err); if (/^HTTP \d+$/.test(msg)) { handleProxyStatus(Number(msg.slice(5))); } else { showError(msg); // data.error: remote MCP server's own message — show verbatim } }

Prevention

When it happens

Trigger: Calling a tool whose server went down after connect (data.error); passing toolName/toolArgs the server rejects — unknown tool or schema-invalid arguments (data.error); losing Pro auth between connect and invoke (401/403, HTTP path); proxy 5xx. Slow tools exceeding 20s abort with TimeoutError instead — this message means a completed HTTP exchange with a bad outcome.

Common situations: Dashboard configs persisting a widget spec whose MCP server credentials expired; toolArgs exported from a different server version (schema drift); panel kept alive across sessions where the stored serverUrl rotted.

Related errors


AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21). Data as JSON: /api/errors/6d47ddfb23adc52b. Report an issue: GitHub.