koala73/worldmonitor · error · Error
DNS ${recordType} lookup failed: HTTP ${response.status}
Error message
DNS ${recordType} lookup failed: HTTP ${response.status} What it means
Thrown by autoVisualize() in the MCP data panel when the widget-agent response stream emits a `data: {"type":"error"}` event instead of completing. The panel POSTs a 3000-char JSON preview of the MCP tool output to widgetAgentUrl() with {prompt, mode:'create', tier:'pro'} and parses newline-delimited `data:` lines; an error event means the server-side widget generation pipeline reported failure mid-stream. The thrown message is the upstream event.message, falling back to the localized 'mcp.visualizationFailed' string when the event carries none. The local catch (src/components/McpDataPanel.ts:259) clears the cached widget and renders the message via showError().
Source
Thrown at api/_notification-webhook-ssrf.ts:196
if (isBlockedNotificationResolvedAddress(hostname)) {
return 'Webhook URL must not point to a private/local address';
}
return null;
}
async function resolveDnsJson(hostname: string, recordType: 'A' | 'AAAA'): Promise<string[]> {
const url = new URL(DNS_JSON_ENDPOINT);
url.searchParams.set('name', hostname);
url.searchParams.set('type', recordType);
const response = await fetch(url, {
headers: {
Accept: 'application/dns-json',
'User-Agent': 'WorldMonitor-Notification-Webhooks/1.0',
},
signal: AbortSignal.timeout(DNS_RESOLUTION_TIMEOUT_MS),
});
if (!response.ok) throw new Error(`DNS ${recordType} lookup failed: HTTP ${response.status}`);
const data = await response.json() as { Status?: number; Answer?: Array<{ type?: number; data?: string }> };
if (data.Status !== 0) throw new Error(`DNS ${recordType} lookup failed: status ${data.Status}`);
const expectedType = recordType === 'A' ? 1 : 28;
return (data.Answer ?? [])
.filter(answer => answer.type === expectedType && typeof answer.data === 'string')
.map(answer => answer.data!);
}
async function defaultResolveHostname(hostname: string): Promise<string[]> {
const records = await Promise.all([
resolveDnsJson(hostname, 'A'),
resolveDnsJson(hostname, 'AAAA'),
]);
return records.flat();
}
/**
* Fail fast at registration when the webhook hostname currently resolves to aView on GitHub (pinned to eeab0a219f)
Solutions
- Read the panel error UI: the thrown Error forwards the upstream event.message, which names the real cause
- Retry the visualization once; transient upstream provider failures usually clear
- Verify widget credentials so the POST carries X-Widget-Key / X-Pro-Key / X-WorldMonitor-Key headers
- Inspect the widgetAgentUrl() POST in the browser Network tab to see the raw error event and confirm the stream shape
- Check widget-agent server logs for the generation failure; if the tool data is huge, test with a smaller payload
Example fix
// before: any stream hiccup shows a generic failure
} else if (event.type === 'error') {
throw new Error(String(event.message ?? t('mcp.visualizationFailed')));
}
// after: only fire when a widget/pro/tester key exists, keeping the upstream message
const hasAgentCreds = getWidgetAgentKey() || getProWidgetKey() || getBrowserTesterKey();
if (!hasAgentCreds) {
this.showError(t('mcp.visualizationUnavailable'));
return;
}
// ... in the stream loop:
} else if (event.type === 'error') {
throw new Error(String(event.message ?? t('mcp.visualizationFailed')));
} Defensive patterns
Strategy: try-catch
Validate before calling
const hasAgentCreds = !!(getWidgetAgentKey() || getProWidgetKey() || getBrowserTesterKey());
if (!hasAgentCreds) {
panel.showError(t('mcp.visualizationUnavailable'));
return;
} Try / catch
try {
await autoVisualize(jsonData, hash);
} catch (err) {
if ((err as { name?: string }).name === 'AbortError') return; // 120s timeout or destroy
const msg = err instanceof Error && err.message ? err.message : t('mcp.visualizationFailed');
panel.showError(msg);
panel.cachedWidgetHtml = null;
} Prevention
- Configure widget/pro or tester keys so requests carry X-Widget-Key/X-Pro-Key/X-WorldMonitor-Key
- Keep the tool JSON preview small; the panel already slices to 3000 chars, avoid re-sending larger blobs
- Mirror the panel's own pattern: filter AbortError before rendering errors
- Watch the widget-agent health endpoint when visualization errors cluster
When it happens
Trigger: POST to widgetAgentUrl() returns HTTP 200 with a body, but the stream sends {"type":"error", message}: upstream LLM/provider failure, rate limiting, prompt safety rejection, tool data the generator cannot chart, or an agent-internal error. Aborts from the 120s timeoutController are filtered (AbortError is ignored at line 260), so only genuine error events surface here.
Common situations: No widget/pro/tester keys configured, so getWidgetAgentKey()/getProWidgetKey()/getBrowserTesterKey() return empty and the request goes out without X-Widget-Key/X-Pro-Key/X-WorldMonitor-Key headers; very large or malformed MCP tool JSON; widget-agent deployment outage or upstream model incident; proxies buffering or cutting the SSE stream mid-generation.
Related errors
- HTTP ${res.status}
- DNS ${recordType} lookup failed: status ${data.Status}
- Webhook URL is not a valid URL
- ${label} HTTP 400
- ${label} HTTP ${response.status}
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/1a4f3dd733f83cd8.
Report an issue: GitHub.