{"record":{"id":"c05c54be005c196f","repo":"koala73/worldmonitor","slug":"label-http-response-status","errorCode":null,"errorMessage":"${label} HTTP ${response.status}","messagePattern":"\\$\\{label\\} HTTP \\$\\{response\\.status\\}","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/mcp/billing-denial.ts","lineNumber":227,"sourceCode":"      const body = JSON.parse(detail) as { error?: unknown; code?: unknown } | null;\n      signatureRejected = (body?.code ?? body?.error) === 'invalid_internal_mcp_signature';\n    } catch {\n      // Unknown or malformed bodies retain the generic status-only error.\n    }\n    if (signatureRejected) {\n      throw new Error(`${label} HTTP 401: invalid_internal_mcp_signature`);\n    }\n  }\n  if (preserveBackoff && (response.status === 429 || response.status === 503)) {\n    throw new ToolBackoffError(response.status, response.headers?.get('Retry-After') ?? null, label);\n  }\n  if (response.status === 400) {\n    const violations = await extractSafeRpcViolations(response);\n    if (violations.length > 0) {\n      throw new RpcValidationError(label, violations);\n    }\n  }\n  throw new Error(`${label} HTTP ${response.status}`);\n}\n","sourceCodeStart":209,"sourceCodeEnd":229,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/api/mcp/billing-denial.ts#L209-L229","documentation":"This is the generic non-ok fallback of assertToolFetchOk in api/mcp/billing-denial.ts: the tool's gateway fetch returned a non-ok status that is neither a billing denial (no X-Billing-Verification marker header with a known code) nor a 400 carrying safe proto violations. The message preserves the '<label> HTTP <status>' contract that dispatch's catch-all and log-severity downgrade key on. Every other classification (BillingDenialError, RpcValidationError) has already been ruled out by the time this throws.","triggerScenarios":"Any tools/call whose _execute fetches a /api/... route and gets 401/403/404/409/429/500/502/503 back: an expired or mis-signed HMAC auth header (401), a lapsed entitlement without the billing marker (403), a route that does not exist in this deployment (404), or an upstream 5xx. Also fires for 400s whose body is HTML, malformed JSON, or has no sanitizable violations array.","commonSituations":"Signature canonicalization drift between buildAuthHeaders and the gateway (method, path, or body hash mismatch). Deploying the MCP registry and the API routes out of sync so a tool POSTs to a path that is not yet deployed. Upstream dependency outage surfacing as 502/503. Env var misconfiguration (wrong secret) producing consistent 401s.","solutions":["Parse the status from the message (/ HTTP (\\d+)$/) — 401/403 means auth/entitlement, 404 means route drift, 5xx means retry later","For 401/403, verify the auth headers: method, full path with query string, and body must match exactly what buildAuthHeaders signed","For 404, confirm the route named in the label exists in the target deployment (check docs/api/ OpenAPI or hit the path directly)","For 5xx/429, retry with backoff honoring any Retry-After header"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isToolHttpError(e) {\n  return e instanceof Error && / HTTP \\d+$/.test(e.message)\n    && !(e instanceof RpcValidationError) && e.name !== 'BillingDenialError';\n}\nfunction httpStatusOf(e) { const m = e.message.match(/HTTP (\\d+)$/); return m ? Number(m[1]) : null; }","tryCatchPattern":"try {\n  const r = await client.callTool(name, args);\n} catch (e) {\n  if (isToolHttpError(e)) {\n    const s = httpStatusOf(e);\n    if (s >= 500 || s === 429) return retryWithBackoff(() => client.callTool(name, args), 3);\n    if (s === 401 || s === 403) throw new Error('Auth/entitlement failure — refresh credentials', { cause: e });\n    throw e; // 4xx other than 401/403: not retryable\n  }\n  throw e;\n}","preventionTips":["Centralize the '<label> HTTP <status>' parse in one helper so status-based routing cannot drift per call site","Smoke-test tool auth (one cheap tools/call) after rotating secrets or changing buildAuthHeaders","Pin registry and API route deployments to the same release so 404 drift cannot reach production callers"],"tags":["mcp","http","gateway","fetch","upstream"],"backgroundTag":"upstream-http-error","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}