Download — Gracefully Broken Pdf

function exportRawData() const formData = collectFormData(); const blob = new Blob([JSON.stringify(formData, null, 2)], type: 'application/json' ); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'report-data.json'; a.click(); URL.revokeObjectURL(url); showToast("Raw data saved – you can retry PDF generation later.");

Return a clean error message before ever calling the PDF engine. Never send an error inside a PDF binary. Use structured responses. Success (200 OK with PDF) Content-Type: application/pdf Content-Disposition: attachment; filename="report.pdf" Failure (200 OK with JSON) Even for errors, use 200 OK to avoid browser download interruption, then handle on frontend.

# DON'T DO THIS output = BytesIO() pdf = canvas.Canvas(output) pdf.drawString(100, 750, "Report") # crash here – user gets zero-byte or partial PDF gracefully broken pdf download

Some browsers treat 4xx/5xx responses as download failures and show generic "Failed – Network error". Step 3: Graceful Failure Response on Frontend When receiving a JSON error instead of a PDF blob, show a user‑friendly overlay.

async function downloadPDF() const response = await fetch('/api/generate-pdf', method: 'POST', body: formData ); const contentType = response.headers.get('content-type'); if (contentType.includes('application/json')) const error = await response.json(); showGracefulFailureDialog(error); return; body: formData )

// Normal PDF download const blob = await response.blob(); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'report.pdf'; a.click(); URL.revokeObjectURL(url);

Content-Type: application/json

// Frontend / API validation example function validatePDFRequest(data) const issues = []; if (!data.content) issues.push("No content provided"); if (data.content?.length > 500_000) issues.push("Content too large (>500k chars)"); if (data.images?.some(img => img.size > 10_000_000)) issues.push("Image exceeds 10MB limit"); return issues;