{"record":{"id":"f0f7f8da7244c3b2","repo":"koala73/worldmonitor","slug":"webhook-url-dns-resolution-returned-no-addresses","errorCode":null,"errorMessage":"Webhook URL DNS resolution returned no addresses","messagePattern":"Webhook URL DNS resolution returned no addresses","errorType":"validation","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"api/_notification-webhook-ssrf.ts","lineNumber":234,"sourceCode":" * connection) because DNS can change after registration.\n */\nexport async function assertNotificationWebhookRegistrationUrlSafe(\n  rawUrl: string,\n  resolveHostname: ResolveHostname = defaultResolveHostname,\n): Promise<void> {\n  const staticError = blockedNotificationWebhookUrlReason(rawUrl);\n  if (staticError) throw new Error(staticError);\n\n  const hostname = new URL(rawUrl).hostname.toLowerCase();\n  if (isIpLiteral(hostname)) return;\n  let resolvedAddresses: string[];\n  try {\n    resolvedAddresses = await resolveHostname(hostname);\n  } catch (error) {\n    const message = error instanceof Error ? error.message : String(error);\n    throw new Error(`Webhook URL DNS resolution failed: ${message}`);\n  }\n  if (!resolvedAddresses.length) throw new Error('Webhook URL DNS resolution returned no addresses');\n  if (resolvedAddresses.some(isBlockedNotificationResolvedAddress)) {\n    throw new Error('Webhook URL must not point to a private/local address');\n  }\n}\n","sourceCodeStart":216,"sourceCodeEnd":239,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/api/_notification-webhook-ssrf.ts#L216-L239","documentation":"Thrown when the DoH resolution call succeeded (no exception) but returned zero A/AAAA records for the hostname — effectively an NXDOMAIN or a domain with no address records. Registration refuses such hostnames because there is no address to SSRF-check or deliver to.","triggerScenarios":"Webhook registration with a hostname that legitimately does not exist, has only CNAME/MX records but no A/AAAA, was decommissioned, or was typo'd. Also reached when a CNAME flattens to nothing.","commonSituations":"Typo in the webhook host (e.g. `https://hook.exmaple.com/`); a domain whose DNS is not yet provisioned; a domain that only has MX records; a recently expired/de-registered domain.","solutions":["Double-check the hostname spelling and the domain's registration status.","Confirm the host has at least one A or AAAA record: `dig +short A <host>` and `dig +short AAAA <host>`.","Provision DNS for the host (add an A/AAAA record at your DNS provider) and retry registration.","If the host is a CNAME, ensure the target chain terminates in an address record."],"exampleFix":"// before\nregisterWebhook('https://typo.exmaple.com/hook')\n// after\nregisterWebhook('https://hook.example.com/hook')","handlingStrategy":"validation","validationCode":"async function hostHasAddressRecord(hostname: string): Promise<boolean> {\n  // Mirror the production resolver (DoH A+AAAA).\n  const r = await fetch(`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(hostname)}&type=A`, {\n    headers: { Accept: 'application/dns-json' },\n    signal: AbortSignal.timeout(2000),\n  });\n  const data = await r.json();\n  return Array.isArray(data?.Answer) && data.Answer.some(a => a?.type === 1);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await assertNotificationWebhookRegistrationUrlSafe(rawUrl);\n} catch (err) {\n  if (err.message === 'Webhook URL DNS resolution returned no addresses') {\n    return res.status(400).json({ error: 'The webhook host has no DNS address record.' });\n  }\n  throw err;\n}","preventionTips":["Verify the host has A/AAAA records with `dig` before registering.","Make sure the domain's DNS is fully provisioned before the first registration attempt.","Surface 'no addresses' as a user-correctable config error, not a retryable outage."],"tags":["webhook","ssrf","dns","validation"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}