srbhr/Resume-Matcher · error · Error
${data.detail || Failed to update API keys (status ${res.sta
Error message
${data.detail || Failed to update API keys (status ${res.status}).} What it means
updateApiKeys (saving per-provider keys via the /config/api-keys endpoint) throws this when the response is not ok. It prefers the backend's `detail` string (FastAPI validation/auth message) and falls back to the generic status-code text when the body isn't JSON. Called from the settings key-management `handleSave`.
Source
Thrown at apps/frontend/lib/api/config.ts:500
if (!res.ok) {
throw new Error(`Failed to load API key status (status ${res.status}).`);
}
return res.json();
}
// Update API keys for one or more providers
export async function updateApiKeys(keys: ApiKeysUpdateRequest): Promise<ApiKeysUpdateResponse> {
const res = await apiFetch('/config/api-keys', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(keys),
});
if (!res.ok) {
const data = await res.json().catch(() => ({}));
throw new Error(data.detail || `Failed to update API keys (status ${res.status}).`);
}
return res.json();
}
// Delete API key for a specific provider
export async function deleteApiKey(provider: ApiKeyProvider): Promise<void> {
const res = await apiFetch(`/config/api-keys/${provider}`, {
method: 'DELETE',
credentials: 'include',
});
if (!res.ok) {
const data = await res.json().catch(() => ({}));
throw new Error(data.detail || `Failed to delete API key (status ${res.status}).`);
}
}
View on GitHub (pinned to 116f9cc3b0)
Solutions
- Read the surfaced detail: it usually names the offending provider/field — correct the key entry and resubmit.
- 401/403 → re-authenticate then retry the save; 404 → fix proxy rewrites/BACKEND_ORIGIN; 5xx → check backend key-store logs.
- Validate the key format client-side against PROVIDER_INFO (prefix/length expectations) before calling updateApiKeys.
- Catch in handleSave and surface the message next to the key form so the user can fix input without losing edits.
Example fix
// before
await updateApiKeys({ openai: keyInput });
// after
if (!/^sk-/.test(keyInput.trim())) {
setKeyError('OpenAI keys start with sk-');
return;
}
try {
await updateApiKeys({ openai: keyInput.trim() });
} catch (e) {
setKeyError(e instanceof Error ? e.message : 'Save failed');
} Defensive patterns
Strategy: validation
Validate before calling
const SUPPORTED = ['openai', 'openai_compatible', 'azure_foundry', 'anthropic', 'openrouter', 'gemini', 'deepseek', 'groq', 'ollama'];
if (Object.keys(keys).some((p) => !SUPPORTED.includes(p))) {
throw new Error('Unknown provider in key update');
} Type guard
function isProviderKeys(x: unknown): x is Record<string, string> {
return typeof x === 'object' && x !== null &&
Object.values(x).every((v) => typeof v === 'string');
} Try / catch
try {
await updateApiKeys(keys);
} catch (e) {
const msg = e instanceof Error ? e.message : 'Save failed';
setKeyError(msg); // backend detail surfaces here
} Prevention
- Validate key format per provider before submit (prefix/length heuristics)
- Trim whitespace/newlines from pasted keys
- Disable save while unauthenticated or session is near expiry
- Keep PROVIDER_INFO in sync with the backend provider enum
When it happens
Trigger: Non-2xx on the API-keys update: 401/403 (session expired, CSRF), 422 (payload violates schema — e.g. unknown provider id or malformed key object), 400 (backend rejects a key, e.g. provider-specific format check or encryption failure), 404 (proxy/route misconfiguration), 500 (key store write failed).
Common situations: Pasting a provider key into the wrong provider slot; saving right after the session timed out; backend upgraded with stricter key validation than the running frontend expects; disk/DB issue preventing persistence of the encrypted key.
Related errors
- ${data.detail || Failed to update prompt config (status ${re
- Failed to load API key status (status ${res.status}).
- ${data.detail || Failed to delete API key (status ${res.stat
- ${data.detail || Failed to clear API keys (status ${res.stat
- ${message = data.detail or Failed to update LLM config (stat
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/704ea93e7f2c4b59.
Report an issue: GitHub.