{"record":{"id":"faefee58ca567db1","repo":"amir20/dozzle","slug":"dispatchers-fetch-failed","errorCode":null,"errorMessage":"dispatchers fetch failed","messagePattern":"dispatchers fetch failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"assets/components/WelcomeModal.vue","lineNumber":550,"sourceCode":"\nasync function createAlerts() {\n  if (creating.value) return;\n  const chosen = activeRules.value;\n  if (chosen.length === 0) {\n    createdCount.value = 0;\n    reportUsage(true);\n    step.value = 3;\n    return;\n  }\n\n  creating.value = true;\n  abortController?.abort();\n  abortController = new AbortController();\n  const signal = abortController.signal;\n\n  try {\n    const dispatchersRes = await fetch(withBase(\"/api/notifications/dispatchers\"), { signal });\n    if (!dispatchersRes.ok) throw new Error(\"dispatchers fetch failed\");\n    const dispatchers: Array<{ id: number; type: string }> = await dispatchersRes.json();\n    const cloud = dispatchers.find((d) => d.type === \"cloud\");\n    if (!cloud) throw new Error(\"cloud dispatcher missing\");\n\n    // Fire rule POSTs in parallel. Partial failure is not cleaned up — if one\n    // rejects, the earlier ones are already saved and the user lands on the\n    // fallback toast path. Acceptable for a welcome modal; the user can edit\n    // or delete rules from /notifications.\n    await Promise.all(\n      chosen.map((rule) =>\n        fetch(withBase(\"/api/notifications/rules\"), {\n          method: \"POST\",\n          headers: { \"Content-Type\": \"application/json\" },\n          signal,\n          body: JSON.stringify({\n            name: rule.ruleName,\n            enabled: true,\n            dispatcherId: cloud.id,","sourceCodeStart":532,"sourceCodeEnd":568,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/assets/components/WelcomeModal.vue#L532-L568","documentation":"WelcomeModal.vue throws this when GET /api/notifications/dispatchers returns a non-ok response during the welcome-modal setup flow. The modal aborts creating notification rules because it cannot determine whether a cloud dispatcher exists.","triggerScenarios":"GET /api/notifications/dispatchers fails with 4xx/5xx or is rejected before completion (note the AbortController signal): auth failure, server error, or the request being aborted when the modal is closed/unmounted.","commonSituations":"User closes the welcome modal mid-setup (AbortController aborts, surfaces as an error here); expired session; notifications API unavailable on k8s/agent deployments without a notification manager.","solutions":["Distinguish AbortError from real HTTP failures and skip the toast on user-initiated aborts","Log res.status and the response body to identify the server-side reason","Verify the user is authenticated before opening the modal flow","On deployments without notification support, hide or skip this welcome setup step"],"exampleFix":"// before\nif (!dispatchersRes.ok) throw new Error(\"dispatchers fetch failed\");\n// after\nif (!dispatchersRes.ok) {\n  if (signal.aborted) return; // user closed the modal, not a real failure\n  throw new Error(`dispatchers fetch failed (HTTP ${dispatchersRes.status})`);\n}","handlingStrategy":"try-catch","validationCode":"if (signal.aborted) return;\nconst res = await fetch(withBase(\"/api/notifications/dispatchers\"), { signal });","typeGuard":"function isAbortError(e: unknown): e is DOMException {\n  return e instanceof DOMException && e.name === \"AbortError\";\n}","tryCatchPattern":"try {\n  const res = await fetch(url, { signal });\n  if (!res.ok) throw new Error(`dispatchers fetch failed (HTTP ${res.status})`);\n} catch (e) {\n  if (isAbortError(e) || signal.aborted) return; // user closed modal\n  showToast({ type: \"error\", message: String(e) });\n}","preventionTips":["Always special-case AbortError when using AbortController with fetch","Check auth/session before starting multi-step modal flows","Hide notification setup steps on deployments without notification support"],"tags":["http","vue","notifications","fetch"],"backgroundTag":"http-request-failed","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"}