koala73/worldmonitor · error · Error
Webhook URL is not a valid URL
Error message
Webhook URL is not a valid URL
What it means
Thrown by blockedNotificationWebhookUrlReason when `new URL(rawUrl)` raises — i.e. the supplied webhook string is not a parseable absolute URL (missing protocol, unencoded spaces, stray characters). It is the first static gate inside assertNotificationWebhookRegistrationUrlSafe, so it fails fast at webhook registration before any DNS work.
Source
Thrown at api/_notification-webhook-ssrf.ts:223
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 a
* private or reserved address. Delivery repeats this check (and pins its
* connection) because DNS can change after registration.
*/
export async function assertNotificationWebhookRegistrationUrlSafe(
rawUrl: string,
resolveHostname: ResolveHostname = defaultResolveHostname,
): Promise<void> {
const staticError = blockedNotificationWebhookUrlReason(rawUrl);
if (staticError) throw new Error(staticError);
const hostname = new URL(rawUrl).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(`Webhook URL DNS resolution failed: ${message}`);
}
if (!resolvedAddresses.length) throw new Error('Webhook URL DNS resolution returned no addresses');
if (resolvedAddresses.some(isBlockedNotificationResolvedAddress)) {
throw new Error('Webhook URL must not point to a private/local address');
}
}
View on GitHub (pinned to ffec79ac33)
Solutions
- Send the URL with an explicit absolute https scheme, e.g. `https://hooks.example.com/webhook`.
- Validate client-side before submission: `new URL(value)` in a try/catch and require protocol === 'https:'.
- Trim and encode the input string before constructing the URL.
- Return a 400 to the caller with a clear message rather than letting the raw error bubble.
Example fix
// before
registerWebhook('hooks.example.com/x')
// after
registerWebhook('https://hooks.example.com/x') Defensive patterns
Strategy: validation
Validate before calling
function isValidWebhookUrl(rawUrl: string): boolean {
try {
const u = new URL(rawUrl);
return u.protocol === 'https:';
} catch {
return false;
}
}
if (!isValidWebhookUrl(input)) return res.status(400).json({ error: 'A valid https:// webhook URL is required.' }); Type guard
function isWebhookUrl(value: unknown): value is string {
if (typeof value !== 'string') return false;
try { return new URL(value).protocol === 'https:'; } catch { return false; }
} Try / catch
try {
await assertNotificationWebhookRegistrationUrlSafe(rawUrl);
} catch (err) {
if (err.message === 'Webhook URL is not a valid URL') {
return res.status(400).json({ error: 'Please enter a valid absolute https:// URL.' });
}
throw err;
} Prevention
- Force the client form to prepend `https://` when the user omits a scheme.
- Run `new URL(value)` client-side before submission and disable the submit button until it parses.
- Return 400 with a clear message for each static-rejection branch so the user knows which rule fired.
When it happens
Trigger: A POST to the notification-webhook registration endpoint with a body whose url field is a bare hostname (e.g. "example.com/hook"), a string with spaces, a missing protocol, or any value the URL constructor rejects.
Common situations: Frontend form submitting the webhook without forcing an https:// prefix; copy-paste that drops the scheme; a misconfigured integrations panel; a test fixture passing a relative path.
Related errors
- Webhook URL must use HTTPS
- Webhook URL must not point to a metadata endpoint
- Webhook URL must not point to a private/local address
- Webhook URL DNS resolution returned no addresses
- Webhook URL DNS resolution failed: ${message}
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/d8454a4b017ba5aa.
Report an issue: GitHub.