vxcontrol/pentagi · error
Unexpected response from server
Error message
Unexpected response from server
What it means
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.
Source
Thrown at frontend/src/lib/axios.ts:187
delete: <T>(url: string, config?: AxiosRequestConfig) => axios.delete<T, ApiResponse<T>>(url, config),
get: <T>(url: string, config?: AxiosRequestConfig) => axios.get<T, ApiResponse<T>>(url, config),
patch: <T, B = unknown>(url: string, body?: B, config?: AxiosRequestConfig) =>
axios.patch<T, ApiResponse<T>, B>(url, body, config),
post: <T, B = unknown>(url: string, body?: B, config?: AxiosRequestConfig) =>
axios.post<T, ApiResponse<T>, B>(url, body, config),
put: <T, B = unknown>(url: string, body?: B, config?: AxiosRequestConfig) =>
axios.put<T, ApiResponse<T>, B>(url, body, config),
};
export const isApiSuccess = <T>(response: ApiResponse<T>): response is ApiSuccessResponse<T> =>
response.status === 'success';
/** Returns `response.data`, or throws if the API marked the response as an error. */
export const unwrapApiResponse = <T>(response: ApiResponse<T>): T => {
if (!isApiSuccess(response) || response.data == null) {
const message = !isApiSuccess(response) ? (response.msg ?? response.error) : undefined;
throw new Error(message ?? 'Unexpected response from server');
}
return response.data;
};
/**
* Extracts a human-readable message from an unknown error thrown by axios calls.
*
* Lookup order:
* 1. `response.data.msg` — backend-provided message,
* 2. `statusFallbacks[status]` — caller-provided defaults per HTTP status,
* 3. `error.message` — generic axios/network message,
* 4. `fallback` — last-resort string.
*/
export const getApiErrorMessage = (
error: unknown,
fallback: string,
statusFallbacks?: Record<number, string>,View on GitHub (pinned to ea665308ba)
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
Example fix
// before
const user = unwrapApiResponse<User>(await api.get('/user'));
// after
const resp = await api.get('/user');
if (!isApiSuccess(resp) || resp.data == null) {
console.error('API failed:', resp.msg ?? resp.error ?? 'no data');
throw new Error(`API failed (${resp.msg ?? 'unknown'})`);
}
const user = resp.data; Defensive patterns
Strategy: try-catch
Validate before calling
const ok = (r: ApiResponse<T>): r is ApiResponse<T> & { data: T } => isApiSuccess(r) && r.data != null; Type guard
function isApiSuccess<T>(r: ApiResponse<T>): r is ApiResponse<T> & { success: true; data: T } {
return r.success === true && r.data != null;
} Try / catch
try {
const data = unwrapApiResponse(resp);
} catch (e) {
if (e.message === 'Unexpected response from server') {
// log resp.status and raw body to find the real cause
console.error('envelope:', resp);
}
throw e;
} Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/d8c92764902b051b.
Report an issue: GitHub.