paperclipai/paperclip · error

${(payload as { error?: string } | null)?.error ?? `Failed t

Error message

${(payload as { error?: string } | null)?.error ?? `Failed to load profile (${res.status})`}

What it means

The auth API's loadProfile method throws this Error when GET /profile returns a non-OK HTTP status. It prefers a server-provided `error` string from the JSON body and falls back to a generic message including the HTTP status. Before throwing, it gives tenantSessionRecovery a chance to handle tenant/session recovery instead of surfacing the error.

Source

Thrown at ui/src/api/auth.ts:189

  signInEmail: async (input: { email: string; password: string }) => {
    await authPost("/sign-in/email", input);
  },

  signUpEmail: async (input: { name: string; email: string; password: string }) => {
    await authPost("/sign-up/email", input);
  },

  getProfile: async (): Promise<CurrentUserProfile> => {
    const res = await fetch("/api/auth/profile", {
      credentials: "include",
      headers: { Accept: "application/json" },
    });
    const payload = await res.json().catch(() => null);
    if (!res.ok) {
      const recovery = tenantSessionRecovery.recoverIfNeeded(res.status, payload);
      if (recovery) return recovery;
      throw new Error((payload as { error?: string } | null)?.error ?? `Failed to load profile (${res.status})`);
    }
    return currentUserProfileSchema.parse(payload);
  },

  updateProfile: async (input: UpdateCurrentUserProfile): Promise<CurrentUserProfile> =>
    authPatch("/profile", input, (payload) => currentUserProfileSchema.parse(payload)),

  signOut: async (): Promise<SignOutResult | null> => {
    const payload = await authPost("/sign-out", {});
    if (!payload || typeof payload !== "object") return null;

    const result = payload as Record<string, unknown>;
    return {
      ...(typeof result.success === "boolean" ? { success: result.success } : {}),
      ...(typeof result.redirectTo === "string" ? { redirectTo: result.redirectTo } : {}),
    };
  },
};

View on GitHub (pinned to 01ad858492)

Solutions

  1. Check network tab for the actual status/body of the profile request
  2. If 401, re-authenticate or let tenantSessionRecovery run its recovery flow
  3. If the body has no `error` field, fix the server route to return `{ error: string }` on failure
  4. Verify the API server is running and /api/health passes

Example fix

// before
throw new Error((payload as { error?: string } | null)?.error ?? `Failed to load profile (${res.status})`);
// after
if (res.status === 401) { await refreshSession(); return loadProfile(); }
throw new Error((payload as { error?: string } | null)?.error ?? `Failed to load profile (${res.status})`);
Defensive patterns

Strategy: try-catch

Validate before calling

const ok = typeof navigator !== 'undefined' && navigator.onLine;

Type guard

function hasServerError(p: unknown): p is { error: string } { return typeof p === 'object' && p !== null && typeof (p as any).error === 'string'; }

Try / catch

try { const profile = await authApi.loadProfile(); } catch (e) { if (/\(401\)/.test(e.message) || /Failed to load profile/.test(e.message)) redirectToLogin(); else showToast(e.message); }

Prevention

When it happens

Trigger: The /api/auth/profile (or equivalent) endpoint returns 401/403/404/500; the response body is not JSON (json().catch returns null), so the fallback message with status is used; the server returns an error envelope `{ error: "..." }`.

Common situations: Expired or invalid session cookie hitting the profile endpoint, user deleted while a tab stays open, API server restarting behind a proxy returning 502, tenant mismatch after an org switch.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/a44f58a2dd99798a. Report an issue: GitHub.