amir20/dozzle · error · Error
Failed to save alert
Error message
Failed to save alert
What it means
saveAlert() POSTs (or PUTs when editing) an alert rule to the notifications API. When the server responds with a non-OK status, it reads the JSON error field from the response body and throws it; the literal string "Failed to save alert" is the fallback used when the response body has no error field or the body is not parseable. The caught message is stored in saveError for display next to the form.
Solutions
- Read saveError.value (or the thrown message) for the server-provided error string and fix the offending field in the form payload.
- Verify the alert payload matches the backend alert schema: required name, at least one pattern/rule, valid channel destinations.
- Check the backend logs for the corresponding request to /api/notifications (or alerts) to see the 4xx/5xx cause, e.g. unwritable notifications.yml.
- If editing, confirm the alert still exists server-side; if it was removed, create a new one instead of PUTting to a stale id.
Example fix
// before
await saveAlert(input); // throws generic "Failed to save alert"
// after
if (!input.name?.trim()) {
saveError.value = "Alert name is required";
return;
}
try {
await saveAlert(input);
} catch (e) {
console.error("alert save rejected by server", e);
} Defensive patterns
Strategy: try-catch
Validate before calling
// before calling saveAlert
function validateAlertInput(input: AlertInput): string | null {
if (!input.name?.trim()) return "Alert name is required";
if (!input.patterns?.length) return "At least one pattern is required";
return null;
} Try / catch
try {
await saveAlert(input);
} catch (e) {
saveError.value = e instanceof Error ? e.message : "Unexpected error saving alert";
} Prevention
- Show saveError.value in the form UI so server messages reach the user.
- Validate required fields client-side before submitting.
- Check the backend logs on persistent failures (permissions on notifications.yml, auth).
- Handle 404 on edit by offering to recreate the alert.
When it happens
Trigger: Calling saveAlert() while the backend returns res.ok === false, e.g. the alert payload fails server-side validation, an alert with the same name already exists (conflict on POST), the alert being edited no longer exists (404 on PUT), or the session is unauthenticated (401).
Common situations: Submitting the alert form with fields the backend rejects (empty name, invalid schedule, bad regex); the data/notifications.yml file is unwritable so the backend persist step fails (500); editing an alert that was deleted in another tab (404); auth configured and the token expired (401).
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
AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07).
Data as JSON: /api/errors/6cc92b42c1c5a208.
Report an issue: GitHub.
Appendix: source
Thrown at assets/composable/alertForm.ts:103
try {
const input = {
name: alertName.value.trim(),
containerExpression: containerExpression.value,
dispatcherId: dispatcherId.value,
enabled: options.alert?.enabled ?? true,
...typeSpecificFields,
};
const url = isEditing.value
? withBase(`/api/notifications/rules/${options.alert!.id}`)
: withBase("/api/notifications/rules");
const res = await fetch(url, {
method: isEditing.value ? "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 alert");
}
options.onCreated?.();
options.close?.();
} catch (e) {
saveError.value = e instanceof Error ? e.message : "Failed to save alert";
} finally {
isSaving.value = false;
}
}
async function validatePreview(extraFields: Record<string, unknown> = {}) {
if (!containerExpression.value && !Object.values(extraFields).some(Boolean)) {
containerResult.value = null;
return { data: null };
}
isLoading.value = true;
try {View on GitHub (pinned to d9463cbe21)