{"record":{"id":"c60f5b8598d4cca7","repo":"mastra-ai/mastra","slug":"request-failed-res-status-server-provided-m-c60f5b","errorCode":null,"errorMessage":"Request failed (${res.status}) / server-provided message","messagePattern":"Request failed \\((.+?)\\) / server-provided message","errorType":"http","errorClass":"RequestError","httpStatus":null,"severity":"error","filePath":"mastracode/factory-ui/src/ui/domains/factory/services/request.ts","lineNumber":27,"sourceCode":"    this.name = 'RequestError';\n  }\n}\n\nexport async function requestJson<T>(url: string, init?: RequestInit): Promise<T> {\n  const headers = new Headers(init?.headers);\n  if (!headers.has('Accept')) headers.set('Accept', 'application/json');\n  if (init?.body && !headers.has('content-type')) headers.set('content-type', 'application/json');\n  const res = await fetch(url, { ...init, headers, credentials: 'include' });\n  if (!res.ok) {\n    let message = `Request failed (${res.status})`;\n    try {\n      const body = (await res.json()) as { error?: string; message?: string };\n      if (body.message) message = body.message;\n      else if (body.error) message = body.error;\n    } catch {\n      /* ignore non-JSON */\n    }\n    throw new RequestError(message, res.status);\n  }\n  return (await res.json()) as T;\n}\n","sourceCodeStart":9,"sourceCodeEnd":31,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory-ui/src/ui/domains/factory/services/request.ts#L9-L31","documentation":"requestJson is the shared JSON-fetch helper in factory-ui's request.ts. When the response is not ok it builds a message from the server JSON body (`message` then `error`, falling back to `Request failed (${res.status})`) and throws a RequestError carrying the HTTP status. All factory attention, comments, and knowledge-graph calls route through it.","triggerScenarios":"Any requestJson call (fetchFactoryAttention, updateFactoryAttentionReceipt, listWorkItemComments, fetchKnowledgeGraph, fetchKnowledgeNode) receiving 4xx/5xx: expired session on GET, invalid patch payload on attention updates, unknown work-item id on comments, or knowledge endpoints returning 404/500.","commonSituations":"Session cookie expired mid-use, work item deleted by another user so comments/knowledge requests 404, malformed update payloads rejected with 400, backend deploy breaking knowledge-graph routes (500), or a proxy returning HTML (non-JSON) so only the generic status message is available.","solutions":["Catch RequestError specifically and branch on its status property (e.g. 401 -> re-login, 404 -> refresh stale data, 5xx -> retry/backoff).","Read the thrown message for the server-provided `error`/`message` and fix the indicated payload or permission problem.","Ensure cookies are included for cross-origin baseUrl (CORS `Access-Control-Allow-Credentials`) to prevent 401s.","If a 404 on ids, refresh the work item / graph list before retrying since the resource no longer exists."],"exampleFix":"// before\nconst graph = await fetchKnowledgeGraph(baseUrl);\n// after\ntry {\n  const graph = await fetchKnowledgeGraph(baseUrl);\n} catch (e) {\n  if (e instanceof RequestError && e.status === 401) redirectToLogin();\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// pre-flight: ensure ids the call depends on exist\nif (!workItemId) throw new Error('workItemId required before listing comments');\nconst exists = await res_ok(`${baseUrl}/web/factory/work-items/${encodeURIComponent(workItemId)}`);\nif (!exists) throw new Error('Work item no longer exists; refresh the list first');","typeGuard":"class RequestError extends Error { constructor(message: string, public status: number) { super(message); } }\nfunction isRequestError(e: unknown): e is RequestError {\n  return e instanceof RequestError;\n}","tryCatchPattern":"try {\n  const graph = await fetchKnowledgeGraph(baseUrl);\n} catch (e) {\n  if (isRequestError(e)) {\n    if (e.status === 401) redirectToLogin();\n    else if (e.status === 404) invalidateAndRefetch();\n    else if (e.status >= 500) queueRetryWithBackoff();\n    else showSnackbar(e.message);\n  } else throw e;\n}","preventionTips":["Always catch RequestError at feature boundaries and branch on `status`.","Re-validate ids/resources before mutating calls like updateFactoryAttentionReceipt.","Use React Query retry policies that skip retrying 4xx and back off on 5xx.","Verify cross-origin deployments send cookies (CORS credentials) to avoid avoidable 401s."],"tags":["http-error","api-response","network","requesterror"],"backgroundTag":"http-request-failed-with-status","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}