srbhr/Resume-Matcher · error · Error
${message = data.detail or Failed to update LLM config (stat
Error message
${message = data.detail or Failed to update LLM config (status ${res.status}).} What it means
updateLlmConfig throws an Error after a non-OK response from PUT/POST /config/llm-api-key. Unlike fetchLlmConfig it first tries to parse the JSON body and use its `detail` field (FastAPI-style error payload), falling back to a generic status message; non-string details are JSON.stringify'd.
Source
Thrown at apps/frontend/lib/api/config.ts:106
credentials: 'include',
body: JSON.stringify(config),
});
if (!res.ok) {
const data = (await res.json().catch(() => ({}))) as { detail?: unknown };
// FastAPI returns `detail` as a string OR a structured object (this
// endpoint now emits {code, field, missing} for a missing Base URL).
// Passing an object straight to `new Error()` renders "[object Object]",
// so serialize explicitly — same treatment as updateFeaturePrompts.
let message: string;
if (typeof data.detail === 'string') {
message = data.detail;
} else if (data.detail) {
message = JSON.stringify(data.detail);
} else {
message = `Failed to update LLM config (status ${res.status}).`;
}
throw new Error(message);
}
return res.json();
}
// Legacy function for backwards compatibility
export async function updateLlmApiKey(value: string): Promise<string> {
const config = await updateLlmConfig({ api_key: value });
return config.api_key ?? '';
}
// Test LLM connection with optional config (for pre-save testing)
export async function testLlmConnection(config?: LLMConfigUpdate): Promise<LLMHealthCheck> {
const options: RequestInit = {
method: 'POST',
credentials: 'include',
};
View on GitHub (pinned to 116f9cc3b0)
Solutions
- Read the thrown message — a FastAPI `detail` string names the exact invalid field; correct that field in the settings form
- Re-login if the status is 401/403 (session expired while the form was open)
- Validate the API key/provider/model combination client-side before saving
- Check backend logs and its request-validation schema if the message is the generic fallback
Example fix
// before
await updateLlmConfig({ provider: 'openai', model: 'gpt-4', apiKey });
// after
if (!apiKey.startsWith('sk-')) { showMsg('Invalid OpenAI API key'); return; }
try { await updateLlmConfig(config); }
catch (e) { showMsg(e.message); } Defensive patterns
Strategy: validation
Validate before calling
function validateLlmConfig(c: LLMConfig): string | null {
if (!c.provider) return 'provider required';
if (!c.apiKey || c.apiKey.length < 8) return 'apiKey missing/short';
if (!c.model) return 'model required';
return null;
} Type guard
function hasDetail(x: unknown): x is { detail: string } {
return typeof x === 'object' && x !== null && 'detail' in x && typeof (x as any).detail === 'string';
} Try / catch
try {
await updateLlmConfig(cfg);
} catch (e) {
// message already carries backend `detail` when available
showToast(e instanceof Error ? e.message : 'Save failed');
} Prevention
- Mirror backend validation rules in the settings form
- Re-check auth before PUT/POST mutations after idle periods
- Validate provider/model/api-key combinations against a known-good list
- Keep frontend config types in sync with the backend schema
When it happens
Trigger: Saving LLM settings when the backend rejects them: 422 invalid provider/model/api-key shape, 401 unauthenticated, 400 invalid key format, or 500 persistence failure. Also fires when the response body is not JSON (data parse fails -> generic message).
Common situations: Typing a malformed API key or selecting a model not valid for the chosen provider; session expired mid-edit; backend validation schema changed after an upgrade.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- ${data.detail || Failed to update feature config (status ${r
- Failed to load LLM config (status ${res.status}).
- Failed to load feature config (status ${res.status}).
- Failed to load language config (status ${res.status}).
- ${data.detail || Failed to update language config (status ${
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/28942100785b0f5e.
Report an issue: GitHub.