srbhr/Resume-Matcher · error · PDFRenderError

str(e) (message from PDFRenderError)

Error message

str(e) (message from PDFRenderError)

What it means

HTTP 503 raised when render_resume_pdf fails with PDFRenderError while snapshotting the cover-letter print page headlessly (e.g. Playwright/Puppeteer). The raw renderer message (str(e)) is passed through as the detail. 503 signals the rendering service is temporarily unavailable or the page failed to render.

Solutions

  1. Check the PDFRenderError message in the 503 response/logs — fix the specific renderer cause (timeout, navigation, selector).
  2. Verify settings.frontend_base_url is reachable from the backend container (curl the print URL).
  3. Ensure the print route /print/cover-letter exists in the deployed frontend and the .cover-letter-print selector is present.
  4. In Docker, install headless-browser system dependencies and confirm the browser binary launches.
  5. Retry — if the renderer was temporarily saturated, a retry with backoff often succeeds.

Example fix

// before
except PDFRenderError as e:
    raise HTTPException(status_code=503, detail=str(e))
// after (retry transient render failures)
except PDFRenderError as e:
    if e.is_transient:
        pdf_bytes = await render_resume_pdf(url, pageSize, selector=".cover-letter-print")
    else:
        raise HTTPException(status_code=503, detail=str(e))
Defensive patterns

Strategy: retry

Validate before calling

const printUrl = `${frontendBaseUrl}/print/cover-letter/${resumeId}`;
const ok = await fetch(printUrl).then(r => r.ok).catch(() => false);
if (!ok) throw new SkipError('print page unreachable — renderer would fail');

Try / catch

try {
  return await withBackoff(() => downloadCoverLetterPdf(resumeId), {retries: 2});
} catch (e) {
  if (e.status === 503) {
    showStatus('PDF renderer temporarily unavailable: ' + e.detail + '. Check frontend_base_url and try again.');
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: The headless browser fails to load {frontend_base_url}/print/cover-letter/{id} — frontend unreachable, page JS error, selector .cover-letter-print absent, navigation timeout, or browser process launch failure.

Common situations: frontend_base_url misconfigured (wrong host/port, http vs https); print route not deployed in the backend's target environment; browser sandbox missing dependencies in Docker (e.g. libnss3); cover-letter content causing client-side render crash; timeouts on large documents.

Related errors


AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28). Data as JSON: /api/errors/783f3c7b98220487. Report an issue: GitHub.

Appendix: source

Thrown at apps/backend/app/routers/resumes.py:2055

    cover_letter = resume.get("cover_letter")
    if not cover_letter:
        raise HTTPException(
            status_code=404, detail="No cover letter found for this resume"
        )

    # Build print URL (same pattern as resume PDF)
    url = f"{settings.frontend_base_url}/print/cover-letter/{resume_id}?pageSize={pageSize}"
    if lang:
        url = f"{url}&lang={lang}"

    # Render PDF with cover letter selector
    try:
        pdf_bytes = await render_resume_pdf(
            url, pageSize, selector=".cover-letter-print"
        )
    except PDFRenderError as e:
        raise HTTPException(status_code=503, detail=str(e))

    headers = {
        "Content-Disposition": f'attachment; filename="cover_letter_{resume_id}.pdf"'
    }
    return Response(content=pdf_bytes, media_type="application/pdf", headers=headers)

View on GitHub (pinned to 116f9cc3b0)