{"record":{"id":"d8c92764902b051b","repo":"vxcontrol/pentagi","slug":"unexpected-response-from-server","errorCode":null,"errorMessage":"Unexpected response from server","messagePattern":"Unexpected response from server","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/axios.ts","lineNumber":187,"sourceCode":"    delete: <T>(url: string, config?: AxiosRequestConfig) => axios.delete<T, ApiResponse<T>>(url, config),\n    get: <T>(url: string, config?: AxiosRequestConfig) => axios.get<T, ApiResponse<T>>(url, config),\n    patch: <T, B = unknown>(url: string, body?: B, config?: AxiosRequestConfig) =>\n        axios.patch<T, ApiResponse<T>, B>(url, body, config),\n    post: <T, B = unknown>(url: string, body?: B, config?: AxiosRequestConfig) =>\n        axios.post<T, ApiResponse<T>, B>(url, body, config),\n    put: <T, B = unknown>(url: string, body?: B, config?: AxiosRequestConfig) =>\n        axios.put<T, ApiResponse<T>, B>(url, body, config),\n};\n\nexport const isApiSuccess = <T>(response: ApiResponse<T>): response is ApiSuccessResponse<T> =>\n    response.status === 'success';\n\n/** Returns `response.data`, or throws if the API marked the response as an error. */\nexport const unwrapApiResponse = <T>(response: ApiResponse<T>): T => {\n    if (!isApiSuccess(response) || response.data == null) {\n        const message = !isApiSuccess(response) ? (response.msg ?? response.error) : undefined;\n\n        throw new Error(message ?? 'Unexpected response from server');\n    }\n\n    return response.data;\n};\n\n/**\n * Extracts a human-readable message from an unknown error thrown by axios calls.\n *\n * Lookup order:\n *   1. `response.data.msg` — backend-provided message,\n *   2. `statusFallbacks[status]` — caller-provided defaults per HTTP status,\n *   3. `error.message` — generic axios/network message,\n *   4. `fallback` — last-resort string.\n */\nexport const getApiErrorMessage = (\n    error: unknown,\n    fallback: string,\n    statusFallbacks?: Record<number, string>,","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/frontend/src/lib/axios.ts#L169-L205","documentation":"unwrapApiResponse extracts response.data from an API envelope, but throws when the envelope is not marked successful or data is null. When the server sends neither a msg nor an error field (or the response shape is entirely unexpected), the fallback message 'Unexpected response from server' is thrown so callers always get an Error instead of undefined data.","triggerScenarios":"Calling any API wrapper that pipes through unwrapApiResponse while the server returns a non-success envelope (success=false), an empty/null data payload on a success envelope, or a malformed/unexpected body (e.g. an HTML error page from a proxy) with no msg/error fields.","commonSituations":"Backend 500s or gateway 502/504 pages bypass the normal {success,msg,data} envelope; GraphQL/REST endpoint version mismatch changes the envelope; an endpoint legitimately returns null data (e.g. no record found) but the caller treats it as success.","solutions":["Inspect the raw network response (status code + body) to see what the server actually returned before the unwrap","Handle the thrown Error in the caller and surface response status/context, since this generic message hides the real cause","If data can legitimately be null, check isApiSuccess and data presence yourself instead of using unwrapApiResponse"],"exampleFix":"// before\nconst user = unwrapApiResponse<User>(await api.get('/user'));\n// after\nconst resp = await api.get('/user');\nif (!isApiSuccess(resp) || resp.data == null) {\n  console.error('API failed:', resp.msg ?? resp.error ?? 'no data');\n  throw new Error(`API failed (${resp.msg ?? 'unknown'})`);\n}\nconst user = resp.data;","handlingStrategy":"try-catch","validationCode":"const ok = (r: ApiResponse<T>): r is ApiResponse<T> & { data: T } => isApiSuccess(r) && r.data != null;","typeGuard":"function isApiSuccess<T>(r: ApiResponse<T>): r is ApiResponse<T> & { success: true; data: T } {\n  return r.success === true && r.data != null;\n}","tryCatchPattern":"try {\n  const data = unwrapApiResponse(resp);\n} catch (e) {\n  if (e.message === 'Unexpected response from server') {\n    // log resp.status and raw body to find the real cause\n    console.error('envelope:', resp);\n  }\n  throw e;\n}","preventionTips":["Check the raw HTTP status/body in devtools before assuming an app bug","Pre-check isApiSuccess and data presence instead of relying on the generic thrown message","Keep frontend and backend envelope contracts in sync (regenerate GraphQL types after schema changes)"],"tags":["frontend","typescript","api-response"],"backgroundTag":"unexpected-api-response","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}