srbhr/Resume-Matcher · error · HTTPException (wraps PDFRenderError)

str(e)

Error message

str(e)

What it means

HTTP 503 raised by download_resume_pdf when the headless-browser PDF renderer throws PDFRenderError; the exception message (str(e)) becomes the response detail. Signals the PDF rendering service could not complete, typically a browser/Chromium launch or page-load failure.

Source

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

        f"&accentColor={accentColor}"
    )
    if lang:
        params = f"{params}&lang={lang}"
    url = f"{settings.frontend_base_url}/print/resumes/{resume_id}?{params}"

    # Use the exact margins provided; compact mode only affects spacing.
    pdf_margins = {
        "top": marginTop,
        "right": marginRight,
        "bottom": marginBottom,
        "left": marginLeft,
    }

    # Render PDF with margins applied to every page
    try:
        pdf_bytes = await render_resume_pdf(url, pageSize, margins=pdf_margins)
    except PDFRenderError as e:
        raise HTTPException(status_code=503, detail=str(e))

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


@router.delete("/{resume_id}")
async def delete_resume(resume_id: str) -> dict:
    """Delete a resume by ID."""
    if not await db.delete_resume(resume_id):
        raise HTTPException(status_code=404, detail="Resume not found")

    return {"message": "Resume deleted successfully"}


@router.post("/{resume_id}/retry-processing", response_model=ResumeUploadResponse)
async def retry_processing(resume_id: str) -> ResumeUploadResponse:
    """Retry AI processing for a failed or stuck resume.

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Read the 503 detail/str(e) and server logs to identify the renderer failure (browser missing, URL unreachable, timeout)
  2. Install the required browser binaries (e.g. `playwright install chromium`) in the deployment image
  3. Verify the print page URL is reachable from the renderer process and add required sandbox/no-sandbox flags
  4. Retry the download; if timeouts recur, increase the renderer timeout or resume size limits

Example fix

# Dockerfile before: no browser installed
RUN pip install -r requirements.txt
# after
RUN pip install -r requirements.txt
RUN playwright install --with-deps chromium
Defensive patterns

Strategy: retry

Try / catch

async function downloadPdfWithRetry(resumeId, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try { return await downloadResumePdf(resumeId); }
    catch (e) {
      if (e.status === 503 && i < attempts - 1) { await sleep(2000 * (i + 1)); continue; }
      if (e.status === 503) throw new Error('PDF service unavailable: ' + e.detail);
      throw e;
    }
  }
}

Prevention

When it happens

Trigger: render_resume_pdf fails because Chromium is not installed or crashed, the print URL is unreachable from the renderer, page load times out, or renderer resource limits (memory) are hit on large resumes.

Common situations: Missing playwright/puppeteer browser binaries in the container image, sandbox flags missing in restricted environments (Docker/CI), print page deployment mismatch so the renderer URL 404s, or cold-start timeouts on serverless.

Related errors


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