koala73/worldmonitor · error · Error

callbackUrl DNS resolution returned no addresses

Error message

callbackUrl DNS resolution returned no addresses

What it means

assertCallbackUrlRegistrationSafe succeeds in reaching the resolver but the combined A+AAAA result is empty, so registration rejects with 'callbackUrl DNS resolution returned no addresses'. The DoH answer had Status 0 but no records survived the type filter (type 1 for A, type 28 for AAAA) — the name resolves authoritatively yet has no address records.

Source

Thrown at server/worldmonitor/shipping/v2/webhook-shared.ts:132

 * keeps this fail-fast check from becoming the only SSRF control.
 */
export async function assertCallbackUrlRegistrationSafe(
  callbackUrl: string,
  resolveHostname: ResolveHostname = defaultResolveHostname,
): Promise<void> {
  const staticError = isBlockedCallbackUrl(callbackUrl);
  if (staticError) throw new Error(staticError);

  const hostname = new URL(callbackUrl).hostname.toLowerCase();
  if (isIpLiteral(hostname)) return;
  let resolvedAddresses: string[];
  try {
    resolvedAddresses = await resolveHostname(hostname);
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    throw new Error(`callbackUrl DNS resolution failed: ${message}`);
  }
  if (!resolvedAddresses.length) throw new Error('callbackUrl DNS resolution returned no addresses');
  const blocked = resolvedAddresses.find(isBlockedResolvedAddress);
  if (blocked) throw new Error('callbackUrl resolves to a private/reserved address');
}

export async function generateSecret(): Promise<string> {
  const bytes = new Uint8Array(32);
  crypto.getRandomValues(bytes);
  return [...bytes].map(b => b.toString(16).padStart(2, '0')).join('');
}

export function generateSubscriberId(): string {
  const bytes = new Uint8Array(12);
  crypto.getRandomValues(bytes);
  return 'wh_' + [...bytes].map(b => b.toString(16).padStart(2, '0')).join('');
}

export function webhookKey(subscriberId: string): string {
  return `webhook:sub:${subscriberId}:v1`;

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Verify the host has at least one A or AAAA record: dig example.com A +short and dig example.com AAAA +short
  2. Add the missing address record at your DNS provider and wait for propagation before registering
  3. Double-check you registered the receiver hostname, not a bare or mail-only subdomain

Example fix

// before
await registerWebhook({ callbackUrl: 'https://mail-only.example.com/cb', chokepointIds });
// after
await registerWebhook({ callbackUrl: 'https://hooks.example.com/cb', chokepointIds }); // host has an A record
Defensive patterns

Strategy: validation

Validate before calling

const a = await resolve(host, 'A'); const aaaa = await resolve(host, 'AAAA');
if (a.length + aaaa.length === 0) throw new RangeError(`${host} has no A/AAAA records — add one before registering`);

Try / catch

catch (e) { if (e?.details?.[0]?.description === 'callbackUrl DNS resolution returned no addresses') { add an A/AAAA record, wait for propagation, re-submit } else throw e; }

Prevention

When it happens

Trigger: Registering a webhook whose hostname exists in DNS but has no A or AAAA records: a domain with only MX/TXT records, a CNAME chain ending without address records, or answers of other types the filter drops. Also possible during DNS propagation windows right after record creation.

Common situations: Parked domains with no address records; apex CNAME configurations that yield no A; a record created minutes ago not yet propagated; hostnames meant only for email or verification.

Understand the failure class

Related errors


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