{"record":{"id":"ba877629cee56b34","repo":"paperclipai/paperclip","slug":"announcement-request-failed","errorCode":null,"errorMessage":"Announcement request failed","messagePattern":"Announcement request failed","errorType":"http","errorClass":"ApiError","httpStatus":null,"severity":"error","filePath":"ui/src/api/announcements.ts","lineNumber":8,"sourceCode":"import { announcementSchema, type Announcement } from \"@paperclipai/shared\";\nimport { ApiError } from \"./client\";\n\nasync function request(path: string, init: RequestInit) {\n  const response = await fetch(`/api/announcements/${path}`, {\n    credentials: \"same-origin\", cache: \"no-store\", ...init,\n  });\n  if (!response.ok) throw new ApiError(\"Announcement request failed\", response.status, null);\n  return response;\n}\n\nexport const announcementsApi = {\n  // Deliberately not coalesced by URL across account changes.\n  async current(signal: AbortSignal): Promise<Announcement | null> {\n    const response = await request(\"current\", { signal });\n    const payload = await response.json();\n    return payload === null ? null : announcementSchema.parse(payload);\n  },\n  async dismiss(id: string, companyId: string, signal: AbortSignal) {\n    await request(`${encodeURIComponent(id)}/dismiss`, {\n      method: \"POST\", signal, headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify({ companyId }),\n    });\n  },\n};\n","sourceCodeStart":1,"sourceCodeEnd":25,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/ui/src/api/announcements.ts#L1-L25","documentation":"The announcements API wrapper performs fetch to /api/announcements/* with same-origin credentials. Any non-OK HTTP response (404, 401, 500, etc.) is converted to ApiError(\"Announcement request failed\", status, null). It is raised from request(), used by current(), list, and dismiss().","triggerScenarios":"Any fetch to /api/announcements/current, /api/announcements/:id/dismiss, etc., where response.ok is false — e.g. session expired (401), unknown announcement id on dismiss (404), or server error (500).","commonSituations":"User session cookie expired so the announcements endpoint returns 401; dismissing an announcement already removed server-side (404); API server down or middleware failing (5xx); base path changed behind a proxy so /api/announcements 404s.","solutions":["Check error.status to branch: 401 → re-authenticate/refresh session; 404 → treat announcement as gone and clear local state; 5xx → retry later.","Verify the API server is reachable at /api and the announcements routes are registered.","On dismiss failures, keep the local dismissal optimistic and reconcile server-side on next load.","If behind a reverse proxy, confirm /api/announcements/* is routed correctly."],"exampleFix":"// before\nif (!response.ok) throw new ApiError(\"Announcement request failed\", response.status, null);\n// after\nif (!response.ok) {\n  const body = await response.text().catch(() => null);\n  throw new ApiError(`Announcement request failed (${response.status})`, response.status, body ? { body } : null);\n}","handlingStrategy":"try-catch","validationCode":"const res = await fetch(\"/api/health\", { credentials: \"same-origin\" });\nif (!res.ok) throw new Error(\"API unreachable — announcements will fail\");","typeGuard":"function isAnnouncementAuthError(e: unknown): e is ApiError {\n  return e instanceof ApiError && e.status === 401;\n}","tryCatchPattern":"try {\n  const announcement = await announcementsApi.current(signal);\n} catch (e) {\n  if (e instanceof ApiError) {\n    if (e.status === 401) redirectToLogin();\n    else if (e.status === 404) clearLocalAnnouncementState();\n    else scheduleRetry();\n    return;\n  }\n  throw e;\n}","preventionTips":["Refresh the session cookie before long-lived tabs hit the announcements endpoint.","Treat 404 on dismiss as success (announcement already gone) instead of surfacing an error.","Verify reverse-proxy routing rules include /api/announcements/* when deploying behind a proxy."],"tags":["http","fetch","announcements"],"backgroundTag":"http-request-failed","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}