koala73/worldmonitor · warning · Error
Webhook URL must not point to a metadata endpoint
Error message
Webhook URL must not point to a metadata endpoint
What it means
saveImportedFramework() enforces a cap of MAX_IMPORTED = 20 user-imported analysis frameworks stored in localStorage under the key 'wm-analysis-frameworks' (built-in frameworks are not counted). When the imported array already holds 20 entries, the save throws before any payload validation. The cap bounds localStorage growth of the analysis framework library.
Solutions
- Delete one or more imported frameworks first via deleteImportedFramework(id) (built-ins cannot be deleted and do not count against the cap)
- Verify the actual count: JSON.parse(localStorage.getItem('wm-analysis-frameworks')).length
- Export and prune: keep only frameworks you actively use, then re-import selectively
- If the visible list looks smaller than 20, clear the stale 'wm-analysis-frameworks' key and re-import what you need
Example fix
// before
saveImportedFramework(fw); // throws when 20 imports already stored
// after
const importedCount = loadFrameworkLibrary().filter(f => !f.isBuiltIn).length;
if (importedCount >= 20) {
promptUserToDeleteFirst();
return;
}
saveImportedFramework(fw); Defensive patterns
Strategy: validation
Validate before calling
const importedCount = loadFrameworkLibrary().filter(f => !f.isBuiltIn).length;
if (importedCount >= 20) { promptDeleteFirst(); return; }
saveImportedFramework(fw); Try / catch
try {
saveImportedFramework(fw);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Library is full')) offerFrameworkDeletionUI();
else throw e;
} Prevention
- Show an import counter (n/20) in the library UI
- Bulk imports should check remaining capacity before starting, not per-item mid-loop
- Periodically prune unused imported frameworks
When it happens
Trigger: Calling saveImportedFramework(fw) when loadFromStorage('wm-analysis-frameworks') already returns 20 items, e.g., importing the 21st framework through the import UI or restoring a backup collection.
Common situations: Power users accumulating imports over time; scripts or tests importing in a loop; stale localStorage from an older version holding more entries than the visible list suggests.
Related errors
- Webhook URL DNS resolution failed
- Webhook URL DNS resolution returned no addresses
- a pull_request payload is required
- A valid country code is required for a resilience score…
- Aircraft identifier is too long
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-08-21).
Data as JSON: /api/errors/ae3665b5f36f3f19.
Report an issue: GitHub.
Appendix: source
Thrown at api/_notification-webhook-ssrf.ts:239
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 7d06c8633d)