jamiepine/voicebox · error · Error
HTTP error! status: ${response.status}
Error message
HTTP error! status: ${response.status} What it means
Thrown by the shared `request<T>()` helper inside ApiClient (client.ts:79-97) whenever a JSON endpoint returns a non-2xx HTTP status. The error body is parsed as `{ detail }` (FastAPI convention); `formatErrorDetail` flattens string/array/object detail into a readable message, falling back to `HTTP error! status: <code>` when no detail is present. Because nearly every JSON method on the client routes through this helper, it is the single chokepoint for all REST failures (health, profiles, generations, history, effects, cloud).
Source
Thrown at app/src/lib/api/client.ts:93
const serverUrl = useServerStore.getState().serverUrl;
return serverUrl;
}
private async request<T>(endpoint: string, options?: RequestInit): Promise<T> {
const url = `${this.getBaseUrl()}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!response.ok) {
const error = await response.json().catch(() => ({
detail: response.statusText,
}));
throw new Error(formatErrorDetail(error.detail, `HTTP error! status: ${response.status}`));
}
return response.json();
}
// Health
async getHealth(): Promise<HealthResponse> {
return this.request<HealthResponse>('/health');
}
// Profiles
async createProfile(data: VoiceProfileCreate): Promise<VoiceProfileResponse> {
return this.request<VoiceProfileResponse>('/profiles', {
method: 'POST',
body: JSON.stringify(data),
});
}
View on GitHub (pinned to 51f49dea19)
Solutions
- Confirm the backend is running and reachable: open `serverUrl + '/health'` in a browser or curl it; a 200 means the base URL is correct.
- Read the thrown message — when `detail` is present it carries the FastAPI validation array joined by ';', which names the exact failing field. Fix the payload field it names.
- If the message is the bare fallback `HTTP error! status: 404`, the endpoint does not exist on the backend — update the backend to match the client version or vice versa.
- For 500s, check the backend process logs (the Python traceback identifies the server-side cause), not the frontend.
- If `serverUrl` is wrong, correct it in the server settings UI which writes `useServerStore.serverUrl`.
Example fix
// before — caller has no status context
await api.createProfile(data);
// after — surface status code for diagnostics
try {
return await api.createProfile(data);
} catch (e) {
const m = String(e?.message ?? e);
throw new Error(`createProfile failed: ${m}`, { cause: e });
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before any request(), confirm the backend is reachable
async function ensureBackend(baseUrl: string) {
const r = await fetch(`${baseUrl}/health`);
if (!r.ok) throw new Error(`Backend unreachable (health ${r.status})`);
return true;
} Type guard
// Narrow a thrown value to a usable message
function isApiError(e: unknown): e is Error {
return e instanceof Error && /HTTP error! status: \d+/.test(e.message);
}
// Extract the numeric status when present
function httpStatusOf(e: unknown): number | null {
const m = String((e as Error)?.message ?? '').match(/status: (\d+)/);
return m ? Number(m[1]) : null;
} Try / catch
try {
return await api.someMethod(payload);
} catch (e) {
const status = httpStatusOf(e);
if (status === 422) showValidationToast((e as Error).message); // FastAPI detail array
else if (status && status >= 500) showRetryToast('Server error, please retry');
else throw e; // unknown — rethrow
} Prevention
- Always validate request payloads client-side before calling to avoid 422 validation errors.
- Confirm `useServerStore.serverUrl` points at a running backend before issuing calls (ping /health on settings change).
- Keep the frontend and backend on matched versions so endpoint paths agree.
- Surface status codes in error toasts so users can distinguish 404/422/500.
When it happens
Trigger: Any `this.request(...)` call — e.g. `getHealth()`, `createProfile()`, `listHistory()`, `getCloudStatus()` — when `getBaseUrl()` (read from `useServerStore.getState().serverUrl`) points at a backend that returns 4xx/5xx. Concretely: 404 when the endpoint path is wrong or the server is a different/older version; 422 when a FastAPI validation error occurs (detail is an array of `{msg,loc,type}`); 500 when the Python backend raises; ECONNREFUSED surfaces before this line as a fetch rejection, but a 502/504 from a proxy reaches here.
Common situations: Backend Voicebox Python server is not running or is on a different port than `serverUrl`; user changed the server URL in settings to a wrong value; backend version mismatch where the frontend expects an endpoint the server does not expose; FastAPI request-body validation failure on a create/update call; GPU/model not loaded causing a 500 during generation; CORS preflight failure manifests as an opaque error but a proxied 5xx reaches this throw.
Related errors
- HTTP ${res.status}
- HTTP ${res.status}
- Channel not found
- {exception message from update_channel (ValueError)}
- {exception message from delete_channel (ValueError)}
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/437016c8ee24675b.
Report an issue: GitHub.