{"record":{"id":"6cc92b42c1c5a208","repo":"amir20/dozzle","slug":"failed-to-save-alert","errorCode":null,"errorMessage":"Failed to save alert","messagePattern":"Failed to save alert","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"assets/composable/alertForm.ts","lineNumber":103,"sourceCode":"    try {\n      const input = {\n        name: alertName.value.trim(),\n        containerExpression: containerExpression.value,\n        dispatcherId: dispatcherId.value,\n        enabled: options.alert?.enabled ?? true,\n        ...typeSpecificFields,\n      };\n      const url = isEditing.value\n        ? withBase(`/api/notifications/rules/${options.alert!.id}`)\n        : withBase(\"/api/notifications/rules\");\n      const res = await fetch(url, {\n        method: isEditing.value ? \"PUT\" : \"POST\",\n        headers: { \"Content-Type\": \"application/json\" },\n        body: JSON.stringify(input),\n      });\n      if (!res.ok) {\n        const data = await res.json();\n        throw new Error(data.error || \"Failed to save alert\");\n      }\n      options.onCreated?.();\n      options.close?.();\n    } catch (e) {\n      saveError.value = e instanceof Error ? e.message : \"Failed to save alert\";\n    } finally {\n      isSaving.value = false;\n    }\n  }\n\n  async function validatePreview(extraFields: Record<string, unknown> = {}) {\n    if (!containerExpression.value && !Object.values(extraFields).some(Boolean)) {\n      containerResult.value = null;\n      return { data: null };\n    }\n\n    isLoading.value = true;\n    try {","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/assets/composable/alertForm.ts#L85-L121","documentation":"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.","triggerScenarios":"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).","commonSituations":"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).","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."],"exampleFix":"// before\nawait saveAlert(input); // throws generic \"Failed to save alert\"\n// after\nif (!input.name?.trim()) {\n  saveError.value = \"Alert name is required\";\n  return;\n}\ntry {\n  await saveAlert(input);\n} catch (e) {\n  console.error(\"alert save rejected by server\", e);\n}","handlingStrategy":"try-catch","validationCode":"// before calling saveAlert\nfunction validateAlertInput(input: AlertInput): string | null {\n  if (!input.name?.trim()) return \"Alert name is required\";\n  if (!input.patterns?.length) return \"At least one pattern is required\";\n  return null;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await saveAlert(input);\n} catch (e) {\n  saveError.value = e instanceof Error ? e.message : \"Unexpected error saving alert\";\n}","preventionTips":["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."],"tags":["network","http","api","validation"],"backgroundTag":"http-error-response","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}