srbhr/Resume-Matcher · error · Error
${text || Resume wizard turn failed with status ${response.s
Error message
${text || Resume wizard turn failed with status ${response.status}} What it means
postResumeWizardTurn in apps/frontend/lib/api/resume-wizard.ts throws this Error when the POST to `/resume-wizard/turn` returns a non-OK response. Unlike the enrichment helpers it reads the raw response TEXT (not JSON detail), so the thrown message may contain an HTML error page or a JSON string. It signals the conversational resume-wizard backend rejected or failed the turn.
Source
Thrown at apps/frontend/lib/api/resume-wizard.ts:110
step: 'intro',
resume_data: emptyResumeData(),
current_question: { text: INTRO_QUESTION, section: 'intro' },
history: [],
asked_count: 0,
inferred_skills: [],
is_complete: false,
progress: { current: 0, total: 8 },
warnings: [],
};
}
export async function postResumeWizardTurn(
payload: ResumeWizardTurnRequest
): Promise<ResumeWizardTurnResponse> {
const response = await apiPost('/resume-wizard/turn', payload);
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new Error(text || `Resume wizard turn failed with status ${response.status}`);
}
return response.json();
}
export async function finalizeResumeWizard(
state: ResumeWizardState
): Promise<ResumeWizardFinalizeResponse> {
const response = await apiPost('/resume-wizard/finalize', { state });
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new Error(text || `Resume wizard finalize failed with status ${response.status}`);
}
return response.json();
}
View on GitHub (pinned to 116f9cc3b0)
Solutions
- Log response.status and the text body; if it looks like HTML, the failure is at a proxy/gateway, not the API itself.
- For 422, validate the ResumeWizardTurnRequest (state present, messages non-empty) before sending.
- For 401, re-authenticate and retry the turn.
- For 429/504, retry with backoff and check backend AI provider limits/timeout config.
- Prefer parsing JSON detail when content-type is JSON so users see a clean message instead of raw text/HTML.
Example fix
// before
const response = await apiPost('/resume-wizard/turn', payload);
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new Error(text || `Resume wizard turn failed with status ${response.status}`);
}
// after
const response = await apiPost('/resume-wizard/turn', payload);
if (!response.ok) {
const text = await response.text().catch(() => '');
let detail = '';
try { detail = JSON.parse(text)?.detail ?? ''; } catch { /* non-JSON */ }
throw new Error(detail || `Resume wizard turn failed with status ${response.status}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!payload?.state) throw new Error('Resume wizard turn requires a state object.'); Type guard
function isWizardTurnRequest(p: unknown): p is ResumeWizardTurnRequest {
return typeof p === 'object' && p !== null && 'state' in p;
} Try / catch
try {
const turn = await postResumeWizardTurn(payload);
} catch (e) {
const msg = e instanceof Error ? e.message : '';
if (/<html|<!doctype/i.test(msg)) showError('Service temporarily unavailable. Please retry.');
else if (msg.includes('status 401')) redirectToLogin();
else showError('The assistant could not process your reply. Please try again.');
} Prevention
- Never render raw error text from this function to users — it can contain HTML error pages.
- Persist wizard state locally after each turn so failures do not lose conversation progress.
- Check the gateway/proxy when messages contain HTML: the API itself may be healthy.
When it happens
Trigger: apiPost('/resume-wizard/turn', payload) resolves with response.ok === false: 401 expired session, 404 unknown wizard/resume state id, 422 payload fails schema validation (missing state, malformed messages), 429 AI rate limit, or 500/504 from the LLM turn processing.
Common situations: A gateway returns an HTML 502 page, so the user sees raw HTML inside the Error message; the wizard state was built by an older frontend version and no longer matches the schema (422); session cookie expired mid-conversation (401); LLM latency causes a gateway timeout (504).
Related errors
- ${data.detail || Failed to regenerate content (status ${res.
- ${text || Resume wizard finalize failed with status ${respon
- Improve failed with status ${response.status}: ${text}
- ${data.detail || Failed to analyze resume (status ${res.stat
- ${data.detail || Failed to generate enhancements (status ${r
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/1f1a5c41e9020cc2.
Report an issue: GitHub.