mastra-ai/mastra · error
Push notification URL resolved to a local or private IP: ${h
Error message
Push notification URL resolved to a local or private IP: ${hostname} What it means
To defeat DNS-rebinding, the sender resolves the hostname itself (DNS lookup with all addresses) and rejects it if any resolved address is local/private, throwing this error before pinning requestUrl.hostname to the first resolved address. The request is never made against an unvalidated IP.
Source
Thrown at packages/server/src/server/a2a/push-notification-sender.ts:104
if (this.options.allowedHosts && !this.options.allowedHosts.includes(hostname)) {
throw new Error(`Push notification host is not allowed: ${hostname}`);
}
if (isDisallowedHostname(hostname)) {
throw new Error(`Push notification URL must not target local or internal hosts: ${hostname}`);
}
if (isDisallowedIpAddress(hostname)) {
throw new Error(`Push notification URL must not target local or private IPs: ${hostname}`);
}
const resolvedAddresses =
isIP(hostname) === 0
? await (this.options.lookup ?? defaultLookup)(hostname, { all: true, verbatim: true })
: [{ address: hostname, family: isIP(hostname) }];
if (resolvedAddresses.some(result => isDisallowedIpAddress(result.address))) {
throw new Error(`Push notification URL resolved to a local or private IP: ${hostname}`);
}
const requestUrl = new URL(url.toString());
requestUrl.hostname = resolvedAddresses[0]!.address;
return {
originalUrl: url,
requestUrl,
hostHeader: url.host,
servername: isIP(hostname) === 0 ? hostname : undefined,
};
}
private async postTaskSnapshot({
requestUrl,
hostHeader,
servername,
headers,View on GitHub (pinned to 75dd419e61)
Solutions
- Fix the DNS record so the hostname resolves to a public IP
- Bypass split-horizon DNS by using a publicly resolvable name for the callback
- Check resolution with `dig`/`nslookup` from the server host to see what addresses come back
- Provide a custom `options.lookup` resolver if your environment requires specific DNS behavior (must still return public IPs)
Example fix
// before $ dig push.example.com -> 10.0.0.7 (internal A record) // after $ dig push.example.com -> 203.0.113.7 (public A record)
Defensive patterns
Strategy: validation
Validate before calling
import { lookup } from 'node:dns/promises';
async function assertResolvesPublic(raw: string) {
const host = new URL(raw).hostname;
const addrs = await lookup(host, { all: true, verbatim: true });
if (addrs.length === 0) throw new Error(`No DNS records for ${host}`);
// additionally assert none are private before registering the push config
} Try / catch
try {
await sender.sendNotifications(task, configs);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Push notification URL resolved to a local or private IP')) {
logger.error('DNS rebinding/split-horizon suspected', { err: err.message });
} else throw err;
} Prevention
- Check DNS resolution from the server host, not your laptop (split-horizon differences)
- Ensure all A/AAAA records for the webhook domain are public
- Keep DNS TTLs sane and re-validate records after infrastructure changes
When it happens
Trigger: A hostname-backed push URL whose DNS currently resolves to a private/loopback/link-local address — e.g. a DNS record pointing at 127.0.0.1, a split-horizon DNS returning 10.x inside the cluster, or a tunneled domain resolving to a CGNAT/private range.
Common situations: DNS-rebinding attempts (attacker-controlled domain flipping to 169.254.169.254), corporate DNS resolving public names to internal IPs, split-horizon setups where the server sees internal answers, or recently changed DNS records pointing at private targets.
Related errors
- Push notification URL must use http or https: ${url.protocol
- Push notification host is not allowed: ${hostname}
- Push notification URL must not target local or internal host
- Push notification URL must not target local or private IPs:
- A2A JSON-RPC response did not include a result
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/6008509f84007017.
Report an issue: GitHub.