paperclipai/paperclip · error · ApiError
Announcement request failed
Error message
Announcement request failed
What it means
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().
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.
Example fix
// before
if (!response.ok) throw new ApiError("Announcement request failed", response.status, null);
// after
if (!response.ok) {
const body = await response.text().catch(() => null);
throw new ApiError(`Announcement request failed (${response.status})`, response.status, body ? { body } : null);
} Defensive patterns
Strategy: try-catch
Validate before calling
const res = await fetch("/api/health", { credentials: "same-origin" });
if (!res.ok) throw new Error("API unreachable — announcements will fail"); Type guard
function isAnnouncementAuthError(e: unknown): e is ApiError {
return e instanceof ApiError && e.status === 401;
} Try / catch
try {
const announcement = await announcementsApi.current(signal);
} catch (e) {
if (e instanceof ApiError) {
if (e.status === 401) redirectToLogin();
else if (e.status === 404) clearLocalAnnouncementState();
else scheduleRetry();
return;
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- | null)?.error ?? `Request failed: `}
- Animation must contain only visual HTML/CSS or inline SVG…
- Anthropic Managed Agents request failed with HTTP
- Artifact download failed: HTTP
- Asset filename must match its SHA-256 digest
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/ba877629cee56b34.
Report an issue: GitHub.
Appendix: source
Thrown at ui/src/api/announcements.ts:8
import { announcementSchema, type Announcement } from "@paperclipai/shared";
import { ApiError } from "./client";
async function request(path: string, init: RequestInit) {
const response = await fetch(`/api/announcements/${path}`, {
credentials: "same-origin", cache: "no-store", ...init,
});
if (!response.ok) throw new ApiError("Announcement request failed", response.status, null);
return response;
}
export const announcementsApi = {
// Deliberately not coalesced by URL across account changes.
async current(signal: AbortSignal): Promise<Announcement | null> {
const response = await request("current", { signal });
const payload = await response.json();
return payload === null ? null : announcementSchema.parse(payload);
},
async dismiss(id: string, companyId: string, signal: AbortSignal) {
await request(`${encodeURIComponent(id)}/dismiss`, {
method: "POST", signal, headers: { "Content-Type": "application/json" }, body: JSON.stringify({ companyId }),
});
},
};
View on GitHub (pinned to 3f1d897a7c)