amir20/dozzle · error · Error
Failed to save destination
Error message
Failed to save destination
What it means
WebhookDestinationForm.vue throws this when the POST that saves a webhook notification destination returns !res.ok. It prefers the server's JSON `error` field and falls back to the literal string 'Failed to save destination'.
Solutions
- Inspect the response status and body in devtools; prefer the server `error` message over the fallback string
- Verify the webhook URL is correct, reachable from the Dozzle host, and uses http/https
- Check server logs for the dispatcher creation failure and that the data directory is writable
- Ensure auth token/cookies are valid so the POST isn't rejected by middleware
Example fix
// before
if (!res.ok) {
const data = await res.json();
throw new Error(data.error || "Failed to save destination");
}
// after
if (!res.ok) {
const data = await res.json().catch(() => ({}));
throw new Error(data.error || `Failed to save destination (HTTP ${res.status})`);
} Defensive patterns
Strategy: validation
Validate before calling
const url = new URL(input.url);
if (!/^https?:$/.test(url.protocol)) throw new Error("Webhook URL must be http/https");
if (!input.name?.trim()) throw new Error("Name is required"); Try / catch
try {
const res = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(input) });
if (!res.ok) {
const data = await res.json().catch(() => ({}));
throw new Error(data.error || `Failed to save destination (HTTP ${res.status})`);
}
} catch (e) {
formError.value = e instanceof Error ? e.message : String(e);
} Prevention
- Validate the webhook URL client-side before POSTing
- Parse the error body defensively (it may be HTML from a proxy)
- Test the webhook endpoint reachability from the host before saving
When it happens
Trigger: POST /api/notifications/dispatchers with webhook payload returns 4xx/5xx: invalid URL, unreachable test request on the server side, malformed body, or persistence failure writing the dispatchers file.
Common situations: Entering a webhook URL that the server cannot validate/reach (private IP, typo, https cert issues); notifications.yml is read-only; request rejected by reverse proxy with 401/404 HTML that isn't JSON.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- dispatchers fetch failed
- rule POST failed
- webhook notification failed
- cloud dispatcher missing
- cloud dispatcher rate limited, retry after
AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07).
Data as JSON: /api/errors/9193013efa9a1288.
Report an issue: GitHub.
Appendix: source
Thrown at assets/components/Notification/WebhookDestinationForm.vue:360
type: "webhook",
url: webhookUrl.value.trim(),
template: template.value.trim() || undefined,
headers: headersToRecord(),
};
const url = props.isEditing
? withBase(`/api/notifications/dispatchers/${props.destination!.id}`)
: withBase("/api/notifications/dispatchers");
const res = await fetch(url, {
method: props.isEditing ? "PUT" : "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(input),
});
if (!res.ok) {
const data = await res.json();
throw new Error(data.error || "Failed to save destination");
}
registerGuard(null);
// Hand the saved destination back so callers can select it right away.
props.onCreated?.(await res.json().catch(() => undefined));
props.close?.();
} catch (e) {
error.value = e instanceof Error ? e.message : "Failed to save destination";
} finally {
isSaving.value = false;
}
}
// Unsaved-changes guard, matching the alert form: a stray Esc should not throw away a template.
const registerGuard = useDrawerCloseGuard();
const confirmingDiscard = ref(false);
const initial = ref<string>();
const snapshot = computed(() =>View on GitHub (pinned to d9463cbe21)