{"record":{"id":"d82a2ce6afe32340","repo":"mastra-ai/mastra","slug":"await-extracterror-res","errorCode":null,"errorMessage":"await extractError(res)","messagePattern":"await extractError\\(res\\)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory-ui/src/api/client.ts","lineNumber":53,"sourceCode":"    // Non-JSON body — fall through to the status-based message.\n  }\n  return `Request failed (${res.status})`;\n}\n\nexport function createApiClient({ baseUrl, fetchImpl }: ApiClientConfig): ApiClient {\n  const doFetch = fetchImpl ?? globalThis.fetch;\n\n  async function request<T>(method: string, path: string, body?: unknown): Promise<T> {\n    // `credentials: 'include'` so cross-site session cookies are sent when the\n    // SPA is hosted on a different origin than the API (platform deploy). It is\n    // a no-op for same-origin local dev.\n    const init: RequestInit = { method, credentials: 'include' };\n    if (body !== undefined) {\n      init.headers = { 'Content-Type': 'application/json' };\n      init.body = JSON.stringify(body);\n    }\n    const res = await doFetch(`${baseUrl}${path}`, init);\n    if (!res.ok) throw new Error(await extractError(res));\n    return (await res.json()) as T;\n  }\n\n  return {\n    get: <T>(path: string) => request<T>('GET', path),\n    put: <T>(path: string, body?: unknown) => request<T>('PUT', path, body),\n    post: <T>(path: string, body?: unknown) => request<T>('POST', path, body),\n    del: <T>(path: string, body?: unknown) => request<T>('DELETE', path, body),\n  };\n}\n","sourceCodeStart":35,"sourceCodeEnd":64,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory-ui/src/api/client.ts#L35-L64","documentation":"The factory-ui API client's `request` helper throws a generic Error whose message is whatever the server returned in the error response body (extracted by `extractError`). It is a catch-all for any non-OK HTTP response from the Factory backend (PUT/GET/POST), so the actual cause is the server-side message.","triggerScenarios":"Any `client.get/put/...` call where `doFetch` resolves with `res.ok === false` (4xx/5xx); the thrown message is the response body text from `extractError(res)`.","commonSituations":"Session expired (401/403) with credentials: 'include' cookie rejected; server 500 during a factory operation; route not found after version mismatch between UI and server; CSRF/cookie issues in embedded contexts.","solutions":["Read the thrown message — it is the server's error text; fix the underlying server-reported cause","Check authentication: ensure the session cookie is valid and you are logged in (401/403)","Verify UI and server versions match (404/405 on newer/older routes)","Inspect server logs for the corresponding 5xx if the message is generic"],"exampleFix":"// before\nconst res = await doFetch(`${baseUrl}${path}`, init);\nif (!res.ok) throw new Error(await extractError(res));\n// after (caller-side)\ntry {\n  await client.put('/factory/settings', body);\n} catch (e) {\n  if (e instanceof Error && /401|unauthor/i.test(e.message)) await reauthenticate();\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// pre-flight: check the endpoint is reachable and authenticated\nconst probe = await fetch(`${baseUrl}/health`, { credentials: 'include' });\nif (!probe.ok) throw new Error(`Factory API unavailable (${probe.status}); ${probe.status === 401 ? 're-authenticate' : 'check server'}`);","typeGuard":null,"tryCatchPattern":"try {\n  await client.put('/factory/project', body);\n} catch (e) {\n  if (e instanceof Error) {\n    if (/401|403|unauthor|forbidden/i.test(e.message)) await reauthenticate();\n    else if (/404/i.test(e.message)) console.error('Route mismatch: check UI/server versions');\n    else throw e; // surface server message (it is extractError output)\n  }\n}","preventionTips":["Always read the thrown message — it contains the server's own error text","Keep UI and server versions in sync to avoid route/schema drift","Handle 401 globally (redirect to login) instead of per-call","Log failed request method+path alongside the message for faster diagnosis"],"tags":["http","api-client","fetch","factory-ui"],"backgroundTag":"http-request-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}