koala73/worldmonitor · error

get-country-intel-brief HTTP

Error message

get-country-intel-brief HTTP ${res.status}${code ? `: ${code}` : ''}

What it means

Plain Error thrown in get_country_intel_brief (api/mcp/registry/rpc-tools.ts:1152): the country intel brief endpoint returned non-ok. Before throwing, the tool parses the body for an 'error' field (usually a string like 'invalid_internal_mcp_signature'; non-string shapes are JSON-stringified) and bounds it to 120 chars — non-JSON bodies are HTML-stripped, whitespace-collapsed, and sliced — so the message carries a safe, Sentry-friendly code without body bloat. Note the documented METHOD DRIFT: this tool POSTs while OpenAPI declares only GET; the gateway routes by path so it works, but proxies enforcing method parity can break it.

Solutions

  1. Read the trailing : <code> — 'invalid_internal_mcp_signature' means the auth signature mismatch; verify method (POST), path, and body match what was signed
  2. Validate country_code as ISO 3166-1 alpha-2 before calling
  3. For 5xx, retry with backoff; for 405, check whether an intermediary enforces the OpenAPI-declared GET on this path
  4. Reproduce with curl POST to the path with the same auth headers to isolate signature vs handler failure
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the country param client-side before the call
if (!/^[A-Za-z]{2}$/.test(countryCode ?? '')) throw new Error('country_code must be ISO 3166-1 alpha-2');

Type guard

function isCountryBriefHttpError(e) {
  return e instanceof Error && /^get-country-intel-brief HTTP \d+/.test(e.message);
}
function briefStatusOf(e) { return Number(e.message.match(/HTTP (\d+)/)?.[1] ?? 0); }
function briefCodeOf(e) { return e.message.match(/: (.+)$/)?.[1] ?? ''; }

Try / catch

try {
  const brief = await client.callTool('get_country_intel_brief', { country: 'DE' });
} catch (e) {
  if (isCountryBriefHttpError(e)) {
    const s = briefStatusOf(e), code = briefCodeOf(e);
    if (s >= 500) return retryWithBackoff(call, 3);
    if (code === 'invalid_internal_mcp_signature') throw new Error('Signature bug: sign the POST method+path+body', { cause: e });
    throw e;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling get_country_intel_brief when the internal MCP signature validation fails (401 with 'invalid_internal_mcp_signature' — signature computed over the wrong method/path/body), an invalid country code produces a 4xx, the handler 5xxs, or a method-enforcing intermediary returns 405.

Common situations: buildAuthHeaders signing GET-shape while the request POSTs (or omitting the body from the signature). Country code not ISO alpha-2. OpenAPI/registry method drift tripping strict gateways. Upstream brief-generation dependency outage.

Related errors


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

Appendix: source

Thrown at api/mcp/registry/rpc-tools.ts:1630

        body: briefBody,
        signal: AbortSignal.timeout(22_000),
      }, execution);
      if (!res.ok) {
        throwIfBillingDenial(res, 'get-country-intel-brief');
        // Surface the gateway's error code in the thrown message so Sentry
        // groups the failure by root cause, not just status. Body reads are
        // best-effort; a read failure must not mask the HTTP status.
        const detail = await res.text().catch(() => '');
        let code = '';
        // `error` is usually a string (for example,
        // `invalid_internal_mcp_signature`), but stringify non-string shapes so
        // object envelopes remain readable. Bound both paths so Sentry titles
        // cannot bloat on a long body.
        try {
          const error = (JSON.parse(detail) as { error?: unknown }).error ?? '';
          code = (typeof error === 'string' ? error : JSON.stringify(error)).slice(0, 120);
        } catch {
          code = detail.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 120);
        }
        throw new Error(`get-country-intel-brief HTTP ${res.status}${code ? `: ${code}` : ''}`);
      }
      const result = await res.json() as Record<string, unknown>;
      const resultSources = collectMcpBriefSources(Array.isArray(result.sources) ? result.sources as DigestItemForBrief[] : [], 6);
      // groundingStories stays [] when the 2 s digest fetch failed above, which
      // is the honest signal: the brief was written without that grounding.
      return {
        ...result,
        sources: resultSources.length > 0 ? resultSources : sources,
        groundingStories,
        ...(digestCoverage ? { digestCoverage } : {}),
      };
    },
    // METHOD DRIFT: _execute POSTs above but OpenAPI declares only GET on this
    // path (verified against docs/api/IntelligenceService.openapi.json). The
    // gateway routes by path, not method, so POST works at runtime. We declare
    // GET here because OpenAPI is the parity test's source-of-truth — fixing

View on GitHub (pinned to e586b8b4b8)