{"record":{"id":"a44f58a2dd99798a","repo":"paperclipai/paperclip","slug":"payload-as-error-string-null-error","errorCode":null,"errorMessage":"${(payload as { error?: string } | null)?.error ?? `Failed to load profile (${res.status})`}","messagePattern":"(.+?) \\| null\\)\\?\\.error \\?\\? `Failed to load profile \\((.+?)\\)`\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ui/src/api/auth.ts","lineNumber":189,"sourceCode":"\n  signInEmail: async (input: { email: string; password: string }) => {\n    await authPost(\"/sign-in/email\", input);\n  },\n\n  signUpEmail: async (input: { name: string; email: string; password: string }) => {\n    await authPost(\"/sign-up/email\", input);\n  },\n\n  getProfile: async (): Promise<CurrentUserProfile> => {\n    const res = await fetch(\"/api/auth/profile\", {\n      credentials: \"include\",\n      headers: { Accept: \"application/json\" },\n    });\n    const payload = await res.json().catch(() => null);\n    if (!res.ok) {\n      const recovery = tenantSessionRecovery.recoverIfNeeded(res.status, payload);\n      if (recovery) return recovery;\n      throw new Error((payload as { error?: string } | null)?.error ?? `Failed to load profile (${res.status})`);\n    }\n    return currentUserProfileSchema.parse(payload);\n  },\n\n  updateProfile: async (input: UpdateCurrentUserProfile): Promise<CurrentUserProfile> =>\n    authPatch(\"/profile\", input, (payload) => currentUserProfileSchema.parse(payload)),\n\n  signOut: async (): Promise<SignOutResult | null> => {\n    const payload = await authPost(\"/sign-out\", {});\n    if (!payload || typeof payload !== \"object\") return null;\n\n    const result = payload as Record<string, unknown>;\n    return {\n      ...(typeof result.success === \"boolean\" ? { success: result.success } : {}),\n      ...(typeof result.redirectTo === \"string\" ? { redirectTo: result.redirectTo } : {}),\n    };\n  },\n};","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/ui/src/api/auth.ts#L171-L207","documentation":"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.","triggerScenarios":"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: \"...\" }`.","commonSituations":"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.","solutions":["Check network tab for the actual status/body of the profile request","If 401, re-authenticate or let tenantSessionRecovery run its recovery flow","If the body has no `error` field, fix the server route to return `{ error: string }` on failure","Verify the API server is running and /api/health passes"],"exampleFix":"// before\nthrow new Error((payload as { error?: string } | null)?.error ?? `Failed to load profile (${res.status})`);\n// after\nif (res.status === 401) { await refreshSession(); return loadProfile(); }\nthrow new Error((payload as { error?: string } | null)?.error ?? `Failed to load profile (${res.status})`);","handlingStrategy":"try-catch","validationCode":"const ok = typeof navigator !== 'undefined' && navigator.onLine;","typeGuard":"function hasServerError(p: unknown): p is { error: string } { return typeof p === 'object' && p !== null && typeof (p as any).error === 'string'; }","tryCatchPattern":"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); }","preventionTips":["Always check res.ok before parsing profile payloads","Keep tenantSessionRecovery wired for 401/403 handling","Return structured `{ error }` bodies from all auth routes","Add retry-with-backoff for 5xx profile loads"],"tags":["http","api","authentication"],"backgroundTag":"http-error-response","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}