{"record":{"id":"1377f47547b71abc","repo":"koala73/worldmonitor","slug":"dns-recordtype-lookup-failed-status-data-sta-1377f4","errorCode":null,"errorMessage":"DNS ${recordType} lookup failed: status ${data.Status}","messagePattern":"DNS (.+?) lookup failed: status (.+?)","errorType":"validation","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"server/worldmonitor/shipping/v2/webhook-shared.ts","lineNumber":101,"sourceCode":"\nasync function defaultResolveHostname(hostname: string): Promise<string[]> {\n  const resolveHostnameForTest = getResolveHostnameForTest();\n  if (resolveHostnameForTest) return resolveHostnameForTest(hostname);\n\n  const resolveRecordType = async (recordType: 'A' | 'AAAA'): Promise<string[]> => {\n    const url = new URL(DNS_JSON_ENDPOINT);\n    url.searchParams.set('name', hostname);\n    url.searchParams.set('type', recordType);\n    const response = await fetch(url, {\n      headers: {\n        Accept: 'application/dns-json',\n        'User-Agent': 'WorldMonitor-ShippingV2-Webhooks/1.0',\n      },\n      signal: AbortSignal.timeout(DNS_RESOLUTION_TIMEOUT_MS),\n    });\n    if (!response.ok) throw new Error(`DNS ${recordType} lookup failed: HTTP ${response.status}`);\n    const data = await response.json() as { Status?: number; Answer?: Array<{ type?: number; data?: string }> };\n    if (data.Status !== 0) throw new Error(`DNS ${recordType} lookup failed: status ${data.Status}`);\n    const expectedType = recordType === 'A' ? 1 : 28;\n    return (data.Answer ?? [])\n      .filter(answer => answer.type === expectedType && typeof answer.data === 'string')\n      .map(answer => answer.data!);\n  };\n  const records = await Promise.all([resolveRecordType('A'), resolveRecordType('AAAA')]);\n  return records.flat();\n}\n\n/**\n * Validate the current DNS answer before storing a webhook. Delivery makes the\n * same check immediately before send and pins the resulting socket, which\n * keeps this fail-fast check from becoming the only SSRF control.\n */\nexport async function assertCallbackUrlRegistrationSafe(\n  callbackUrl: string,\n  resolveHostname: ResolveHostname = defaultResolveHostname,\n): Promise<void> {","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/server/worldmonitor/shipping/v2/webhook-shared.ts#L83-L119","documentation":"The Cloudflare DoH JSON response's Status field is the DNS RCODE; anything other than 0 makes resolveRecordType throw (2=SERVFAIL, 3=NXDOMAIN), which registration later wraps into 'callbackUrl DNS resolution failed: DNS {A|AAAA} lookup failed: status {rcode}'. It means the resolver authoritatively answered that the callback hostname cannot be resolved.","triggerScenarios":"Registering a webhook whose hostname does not exist publicly (status 3 NXDOMAIN) or whose authoritative nameservers are failing (status 2 SERVFAIL): typo'd domain, recently deleted DNS record, or a split-horizon name that only exists inside a private network.","commonSituations":"Staging domain never created in public DNS; internal hostname (hooks.corp.internal) used as a public callback URL; DNS record removed after copying an old config; nameserver misconfiguration at the registrar.","solutions":["Verify the hostname resolves on the public internet: dig +short example.com A or a DoH lookup in a browser","Fix the typo or create the missing A/AAAA record at your DNS provider and wait for propagation","If the host only exists internally, switch to a public hostname — the SSRF policy would block a private target anyway"],"exampleFix":"// before\nawait registerWebhook({ callbackUrl: 'https://hooks.corp.internal/cb', chokepointIds });\n// after\nawait registerWebhook({ callbackUrl: 'https://hooks.example.com/cb', chokepointIds }); // publicly resolvable","handlingStrategy":"validation","validationCode":"// pre-resolve before registering\nconst r = await fetch(`https://cloudflare-dns.com/dns-query?name=${host}&type=A`, { headers: { Accept: 'application/dns-json' } });\nif ((await r.json()).Status !== 0) fail early — fix DNS before the RPC;","typeGuard":null,"tryCatchPattern":"catch (e) { if (/status 3/.test(e?.details?.[0]?.description ?? '')) { stop retrying — hostname does not exist; fix DNS } else throw e; }","preventionTips":["Verify the callback hostname resolves publicly (dig A/AAAA) before registering","Never use internal-only hostnames as public callback URLs","Re-check DNS after domain migrations or record deletions"],"tags":["dns","nxdomain","servfail","doh","webhooks","registration"],"backgroundTag":"dns-nxdomain","analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}