alan2207/bulletproof-react · error · Error

${message}

Error message

${message}

What it means

This is the api client's fetchApi wrapper in apps/nextjs-app/src/lib/api-client.ts:95 firing on any non-2xx response. It first surfaces the server's message via the notifications store (browser only), then throws a plain Error whose message is `response.message` from the JSON body or `response.statusText` as fallback. All calls through `api.get/post/put/patch/delete` reject this way, so callers must catch it to handle API failures.

Source

Thrown at apps/nextjs-app/src/lib/api-client.ts:95

      ...headers,
      ...(cookieHeader ? { Cookie: cookieHeader } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
    credentials: 'include',
    cache,
    next,
  });

  if (!response.ok) {
    const message = (await response.json()).message || response.statusText;
    if (typeof window !== 'undefined') {
      useNotifications.getState().addNotification({
        type: 'error',
        title: 'Error',
        message,
      });
    }
    throw new Error(message);
  }

  return response.json();
}

export const api = {
  get<T>(url: string, options?: RequestOptions): Promise<T> {
    return fetchApi<T>(url, { ...options, method: 'GET' });
  },
  post<T>(url: string, body?: any, options?: RequestOptions): Promise<T> {
    return fetchApi<T>(url, { ...options, method: 'POST', body });
  },
  put<T>(url: string, body?: any, options?: RequestOptions): Promise<T> {
    return fetchApi<T>(url, { ...options, method: 'PUT', body });
  },
  patch<T>(url: string, body?: any, options?: RequestOptions): Promise<T> {
    return fetchApi<T>(url, { ...options, method: 'PATCH', body });
  },

View on GitHub (pinned to 9506629ed0)

Solutions

  1. Read error.message — it mirrors the server's response message — and fix the underlying request (auth, payload, URL) that produced the non-2xx status.
  2. If it's a 401, your session expired: refresh/re-authenticate and retry; the API client relies on HttpOnly cookies, so check that credentials:'include' requests actually carry them.
  3. Verify API_URL in apps/nextjs-app/.env.local points to a running backend (or set NEXT_PUBLIC_ENABLE_API_MOCKING=true to use MSW mocks).
  4. In React Query consumers, handle the rejection via the mutation/query error callback or an ErrorBoundary instead of letting it bubble uncaught.

Example fix

// before
await api.post('/discussions', { body: payload }); // uncaught Error on 4xx/5xx

// after
try {
  const discussion = await api.post('/discussions', { body: payload });
} catch (e) {
  // message is the server-provided message or statusText
  console.error((e as Error).message);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate payload with Zod before calling the API to avoid 400s
import { Schema } from './schema';
const parsed = Schema.safeParse(payload);
if (!parsed.success) {
  // surface form errors instead of hitting the API
  console.error(parsed.error.flatten().fieldErrors);
} else {
  await api.post('/discussions', { body: parsed.data });
}

Type guard

import { ZodError } from 'zod';
const isApiError = (e: unknown): e is Error & { message: string } =>
  e instanceof Error && e.message.length > 0;

Try / catch

try {
  const data = await api.get('/discussions');
} catch (error) {
  // message mirrors the server response message (or statusText)
  showToast((error as Error).message);
  // optionally inspect status if you extend fetchApi to attach it
}

Prevention

When it happens

Trigger: Any request to `${env.API_URL}<url>` returning a non-ok status: 401 from an expired/missing auth cookie, 400 from Zod validation on the server, 404 for a missing resource, or 500 from a thrown server error. Also occurs when API_URL points to the wrong host/backend so every request 404s/502s, or when the mock API (MSW) is disabled but no real backend is running.

Common situations: Sitting idle until the auth cookie/JWT expires and then every mutation 401s; the API server not running locally while ENABLE_API_MOCKING=false; a misconfigured API_URL in .env.local pointing at a stale deployment; server-side Zod schema stricter than client form validation causing 400s.

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.


AI-assisted analysis of alan2207/bulletproof-react@9506629ed0 (2026-08-27). Data as JSON: /api/errors/aad3a20f4830b480. Report an issue: GitHub.