{"record":{"id":"a89edc0170f78d98","repo":"mastra-ai/mastra","slug":"gateway-api-error-res-status-body","errorCode":null,"errorMessage":"Gateway API error ${res.status}: ${body}","messagePattern":"Gateway API error (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/server/handlers/gateway-memory-client.ts","lineNumber":98,"sourceCode":"\n  private async request<T>(path: string, options: RequestInit = {}): Promise<T> {\n    const url = `${this.baseUrl}${path}`;\n    const controller = new AbortController();\n    const timeout = setTimeout(() => controller.abort(), 10_000);\n    try {\n      const res = await fetch(url, {\n        ...options,\n        signal: options.signal ?? controller.signal,\n        headers: {\n          'Content-Type': 'application/json',\n          Authorization: `Bearer ${this.apiKey}`,\n          ...((options.headers as Record<string, string>) || {}),\n        },\n      });\n\n      if (!res.ok) {\n        const body = await res.text().catch(() => '');\n        throw new Error(`Gateway API error ${res.status}: ${body}`);\n      }\n\n      return res.json() as Promise<T>;\n    } finally {\n      clearTimeout(timeout);\n    }\n  }\n\n  // ── Threads ──────────────────────────────────────────────────\n\n  async listThreads(params: {\n    resourceId?: string;\n    limit?: number;\n    offset?: number;\n  }): Promise<{ threads: GatewayThread[]; total: number }> {\n    const query = new URLSearchParams();\n    if (params.resourceId) query.set('resourceId', params.resourceId);\n    if (params.limit != null) query.set('limit', String(params.limit));","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/handlers/gateway-memory-client.ts#L80-L116","documentation":"The gateway-memory-client request() helper performs fetch calls against the Mastra Gateway memory API and throws a plain Error (`Gateway API error ${res.status}: ${body}`) whenever the response is not ok, embedding the HTTP status and the raw response body. It is a transport-level guard: any 4xx/5xx from the gateway (auth failures, missing threads, gateway downtime) is surfaced uniformly to the listThreads/getThread/createThread/updateThread/deleteThread/listMessages callers. The error is not typed per status, so callers must parse the message to branch.","triggerScenarios":"Any memory client operation (listThreads, getThread, createThread, updateThread, deleteThread, listMessages) where the gateway responds with a non-ok status, e.g. 401 bad API key, 404 unknown thread/resource ID, 429 rate limit, 5xx gateway failure; also when the response body cannot be read (body defaults to empty string).","commonSituations":"Expired or wrong gateway credentials; client and gateway version mismatch so an endpoint route no longer exists (404); gateway behind a proxy returning 502/503 during deploys; requesting a thread ID that was deleted from another session.","solutions":["Parse the status from the message (regex on 'Gateway API error <status>') and branch: 401/403 fix auth, 404 verify the resource ID, 5xx retry with backoff.","Verify the gateway base URL and that the gateway service is running and reachable at that URL.","Check the gateway server logs for the corresponding request; the body in the message usually contains the upstream error detail.","Confirm client/server versions are compatible (matching mastra versions) so the memory API routes exist."],"exampleFix":"// before: treating all gateway failures the same\ntry { await client.listThreads(); } catch { alert('failed'); }\n// after: branch on the embedded status and retry transient failures\ntry {\n  await client.listThreads();\n} catch (e) {\n  const m = /Gateway API error (\\d+)/.exec(e.message);\n  const status = m ? Number(m[1]) : 0;\n  if (status >= 500 || status === 429) return retryWithBackoff(() => client.listThreads());\n  throw e;\n}","handlingStrategy":"retry","validationCode":"// health-check the gateway before issuing memory calls\nconst health = await fetch(`${gatewayBaseUrl}/health`);\nif (!health.ok) throw new Error(`Gateway unreachable: ${health.status}`);","typeGuard":"function isGatewayApiError(e: unknown): e is Error & { status?: number } {\n  if (!(e instanceof Error)) return false;\n  const m = /Gateway API error (\\d+)/.exec(e.message);\n  if (m) (e as any).status = Number(m[1]);\n  return !!m;\n}","tryCatchPattern":"try {\n  const threads = await memoryClient.listThreads();\n} catch (e) {\n  if (isGatewayApiError(e)) {\n    const status = (e as any).status;\n    if (status === 401 || status === 403) return reauthenticateAndRetry();\n    if (status === 404) return handleMissingResource(e);\n    if (status >= 500 || status === 429) return retryWithBackoff(() => memoryClient.listThreads());\n  }\n  throw e;\n}","preventionTips":["Add a small wrapper client that parses the status out of the message and attaches it as e.status.","Retry only transient statuses (429, 5xx) with exponential backoff; never retry 4xx auth/not-found.","Keep client and gateway mastra versions aligned so memory routes always exist.","Verify gateway credentials and base URL in each environment's config before deploy."],"tags":["network","http","gateway","memory"],"backgroundTag":"gateway-api-error","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}