{"record":{"id":"16e96673c6298292","repo":"srbhr/Resume-Matcher","slug":"fallback-status-res-status","errorCode":null,"errorMessage":"${fallback} (status ${res.status}).","messagePattern":"(.+?) \\(status (.+?)\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/tracker.ts","lineNumber":97,"sourceCode":"      .filter((m): m is string => Boolean(m));\n    if (messages.length > 0) return messages.join('; ');\n  }\n  // A dict detail (e.g. HTTPException(detail={...})) — stringify so it reads as\n  // something rather than \"[object Object]\".\n  if (detail && typeof detail === 'object' && !Array.isArray(detail)) {\n    try {\n      return JSON.stringify(detail);\n    } catch {\n      return null;\n    }\n  }\n  return null;\n}\n\nasync function asJson<T>(res: Response, fallback: string): Promise<T> {\n  if (!res.ok) {\n    const data = await res.json().catch(() => ({}));\n    throw new Error(extractDetail(data) || `${fallback} (status ${res.status}).`);\n  }\n  return res.json() as Promise<T>;\n}\n\n// List all applications grouped by status column.\nexport async function listApplications(): Promise<ApplicationListResponse> {\n  const res = await apiFetch('/applications', { credentials: 'include' });\n  return asJson<ApplicationListResponse>(res, 'Failed to load applications');\n}\n\n// Manually add a card from a pasted job description.\nexport async function createApplication(payload: ManualApplicationCreate): Promise<Application> {\n  const res = await apiPost('/applications', payload);\n  return asJson<Application>(res, 'Failed to create application');\n}\n\n// Fetch a card with its embedded JD + applied resume (for the modal).\nexport async function getApplicationDetail(id: string): Promise<ApplicationDetail> {","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/tracker.ts#L79-L115","documentation":"asJson is the shared response-unwrapping helper for the tracker API module; when a response is not ok it throws this Error using either the backend's error detail (extractDetail) or the supplied fallback string plus the HTTP status. Every tracker call (list, create, get, update, bulk update, bulk delete) funnels through it, so any tracker API failure surfaces here.","triggerScenarios":"Any non-2xx tracker response where the body has no parseable 'detail' field: 401 expired session, 404 application not found, 400 invalid payload on create/update, 500 server error, or network-level failure rendered as an error Response.","commonSituations":"Seen when the fallback text like 'Failed to list applications' appears with a status code: backend down during local dev, tracker API route changed/moved, malformed create/update payload, or auth cookie missing in a fresh browser session.","solutions":["Read the fallback text and status to identify which tracker call failed and why","Ensure the backend API is running and its base URL/proxy is configured correctly","Re-authenticate for 401 responses","Validate the request payload (application status values, ids) for 400 errors","Check for backend detail fields in responses so extractDetail returns a specific reason instead of the fallback"],"exampleFix":"// before\nconst list = await listApplications();\n// after\ntry {\n  const list = await listApplications();\n} catch (e) {\n  showBanner(`Applications unavailable: ${e.message}`);\n}","handlingStrategy":"try-catch","validationCode":"// Validate payloads before any tracker mutation that flows through asJson\nfunction validateApplicationInput(input: { company: string; status: string }): string | null {\n  if (!input.company.trim()) return 'Company is required';\n  const allowed = ['wishlist', 'applied', 'interview', 'offer', 'rejected'];\n  if (!allowed.includes(input.status)) return `Invalid status: ${input.status}`;\n  return null;\n}","typeGuard":"function isTrackerApiError(e: unknown): e is Error & { trackerCall?: string } {\n  return e instanceof Error && e.message.includes('(status');\n}","tryCatchPattern":"try {\n  const data = await listApplications();\n  render(data);\n} catch (e) {\n  if (e instanceof Error && /status 401/.test(e.message)) {\n    redirectToLogin();\n  } else if (e instanceof Error && /status 5\\d\\d/.test(e.message)) {\n    showError('Tracker service temporarily unavailable. Retrying...');\n  } else {\n    showError(e instanceof Error ? e.message : 'Tracker request failed.');\n  }\n}","preventionTips":["Ensure the backend includes a 'detail' field in error bodies so extractDetail yields a specific reason instead of the generic fallback","Check backend base URL/proxy configuration when all tracker calls fail together","Validate create/update payloads client-side to avoid 400s","Handle session expiry globally (401) before user sees the fallback error","Add a global fetch wrapper that logs status and body for every non-2xx response"],"tags":["http-error","api","frontend","tracker"],"backgroundTag":"http-non-2xx-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}