What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Trace the PDF through three checkpoints: open the bytes returned by Puppeteer before upload, preserve those bytes when calling Supabase Storage, then download the stored object through the correct public or private access path and compare it with the original. This isolates whether the problem is PDF generation, upload or retrieval; an upload response by itself does not prove the downloaded file is valid.
Find which stage is failing
Work from the generated PDF outward. First save or inspect the result of page.pdf(). Next verify the exact body and response from the Storage upload. Finally fetch the intended object and compare its bytes with the generated file. Do not start by changing MIME headers or downgrading packages: neither is a diagnosis, and a header cannot repair malformed PDF bytes.
| Checkpoint | Test | What a failure suggests |
|---|---|---|
| Generated artifact | Open or validate the PDF immediately after Page.pdf(). |
Page readiness, generation inputs, or Puppeteer/browser runtime. |
| Upload boundary | Preserve the binary body and inspect the complete Supabase upload result. | Text/JSON conversion, request metadata, authorization, limits, or wrong object path. |
| Retrieved artifact | Download through the correct access route and compare its bytes with the original. | Wrong object, access route, cache/overwrite, or response handling. |
This is a diagnostic framework, not a claim that one particular layer is responsible. Puppeteer documents Page.pdf() as returning a Promise<Uint8Array> (API reference). Supabase’s upload and download APIs define separate Storage boundaries, so test each one independently (upload, download).
1. Check the PDF before uploading
Save the returned data locally or write it to a temporary file, then open that file in a PDF viewer. If it already fails locally, Supabase has not yet entered the chain. Check whether PDF generation threw an exception, whether the page has the intended content, and whether the generated file is the one your code later uploads.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errors#1 Best Overall
- Get NVMe solid state performance with up to 1050MB/s read and 1000MB/s write speeds in a portable, high-capacity drive(1) (Based on internal testing; performance may be lower depending on host device & other factors. 1MB=1,000,000 bytes.)
- Up to 3-meter drop protection and IP65 water and dust resistance mean this tough drive can take a beating(3) (Previously rated for 2-meter drop protection and IP55 rating. Now qualified for the higher, stated specs.)
- Use the handy carabiner loop to secure it to your belt loop or backpack for extra peace of mind.
- Help keep private content private with the included password protection featuring 256‐bit AES hardware encryption.(3)
- Easily manage files and automatically free up space with the SanDisk Memory Zone app.(5). Non-Operating Temperature -20°C to 85°C
const pdfBytes = await page.pdf();
console.log({
constructor: pdfBytes?.constructor?.name,
byteLength: pdfBytes?.byteLength,
});
// Diagnostic only: save the exact bytes produced by Puppeteer.
const fs = await import('node:fs/promises');
await fs.writeFile('/tmp/puppeteer-output.pdf', pdfBytes);
Keep the diagnostic file and upload input identical. Avoid converting the result to a UTF-8 string, interpolating it into a string, or JSON-serializing it as if that preserved PDF bytes. A PDF is binary data; text conversions can change byte values and make the file unreadable.
Check what Puppeteer is actually rendering
Puppeteer generates PDFs using print CSS by default. If your page only looks right with screen styles, call page.emulateMediaType('screen') before generating. Its PDF guide also says font loading is awaited by default; if your page uses web fonts or asynchronous content, verify that the relevant assets and content are ready before calling page.pdf() (PDF generation guide).
Options such as format, explicit page dimensions, printBackground, waitForFonts, timeout, and path control rendering, readiness, or output location. They can help explain missing or unexpected page content, but they do not establish that Storage corrupted the file. See Puppeteer’s PDF options reference for the installed API’s documented options.
Rank #2
- Solid state performance with up to 800MB/s read speeds in a portable drive. (Based on internal testing; performance may be lower depending on host device, interface, usage conditions and other factors. 1MB=1,000,000 bytes.)
- Back up your content and memories on a storage solution that fits seamlessly into your mobile lifestyle.
- Take it with you on your adventures—up to two-meter drop protection means this durable drive can take a beating. (Based on internal testing.)
- Secure it to your belt loop or backpack for extra peace of mind thanks to the tough rubber hook.
- From Sandisk, a brand professional photographers trust to take on assignments.
2. Preserve the bytes at the Supabase upload boundary
Pass the PDF data as a supported binary body for the exact @supabase/supabase-js version and runtime in your application. Set the MIME type explicitly to application/pdf. Supabase documents a file-body argument and a contentType upload option; its examples include an ArrayBuffer body (JavaScript upload reference).
const { data, error } = await supabase.storage
.from('documents')
.upload('reports/report.pdf', pdfBytes, {
contentType: 'application/pdf',
upsert: true,
});
if (error) {
console.error('PDF upload failed:', error);
throw error;
}
console.log('Uploaded object:', data);
The snippet assumes pdfBytes is the unmodified result of page.pdf(), and that the bucket is named documents. Substitute your bucket and object path. Confirm the installed SDK accepts the specific body type you pass. If your code wraps the bytes in a Node Buffer, Blob, or another object, check compatibility against the version actually installed; the cited upload reference does not guarantee every runtime-specific combination.
Do not treat contentType as a byte conversion. It labels the uploaded object; it does not transform text back into the original PDF. If you previously encoded the PDF as base64, you must decode it into bytes before upload rather than send the base64 characters as the file body.
Rank #3
- Capacity Display Variance: 500GB external ssd often appears as around 465GB on Windows. MacOS can show full 500 GB capacity. This is binary calculation difference and doesn’t affect SSD hard drive actual physical storage
- 1050 MB/s Speed: Instantly access to your files with blazing-fast 10Gbps external SSD read up to 1050MB/s and write up to 1000MB/s. LED Light indicates USB SSD instant activity
- Data Security: Solid state drives S.M.A.R.T. health diagnostics and adaptive TRIM optimizing data block management ensures consistent write speeds and extends the longevity of the portable SSD
- USB-C & USB-A Cable: Both cables featuring rapid USB 3.2 Gen2, this USB SSD effortlessly bridges devices, enabling seamless cross-platform file transfers and backup between computers, smartphones, tablets and iPhone
- Always Fast: No slowdowns for large file transfers. With SLC caching (25% of current available capacity allocated as high-speed cache), this external SSD delivers steady 10Gbps for transfers within the cache capacity
3. Confirm the upload really succeeded
Inspect the returned error object, not just whether the request completed. Supabase documents distinct Storage failures for cases such as invalid MIME type, missing bucket or object, authorization, and file-size limits. Those are different from a stored object whose bytes are corrupt. Consult the Storage error codes and fix the specific reported condition before comparing PDF contents.
- Log the complete error details during diagnosis; do not log secrets or authorization tokens.
- Verify the bucket name and exact object path used by the upload.
- Check bucket policies and credentials for the operation your server or client performs.
- Check whether an existing object is being overwritten, and ensure the path you later download is the same path you uploaded.
A request may have failed for permissions, validation, or size reasons without ever storing a valid file. Conversely, a successful upload only confirms that Storage accepted the request; it does not verify what a later viewer received.
Recommended Free Tools
4. Download the correct object using the matching access path
First establish whether the bucket is public or private. Supabase documents public URL generation for public assets and notes that a download query parameter can prompt a browser download. A private object is not readable through a public URL: use an authorized download request or a time-limited signed URL. See Supabase’s asset serving guide and the JavaScript download reference.
Rank #4
- NEARLY 2X FASTER THAN OUR PREVIOUS GENERATION(8) – move 1,000 high-res photos in under 60 seconds(6) with up to 2000MB/s transfer speeds(2).
- IP65 RATING AND UP TO 3M DROP PROTECTION(3) – protects against spills and drops.
- POCKET-SIZED – fits easily in pockets and small bags.
- SPACE TO OWN YOUR AI CONTENT – speed and capacity to download your high-res clips and photo edits.
- 256-BIT AES ENCRYPTION(4) – helps keep private files secure with password protection.
// Private bucket: retrieve through an authorized Supabase client.
const { data: downloaded, error: downloadError } = await supabase.storage
.from('documents')
.download('reports/report.pdf');
if (downloadError) {
console.error('PDF download failed:', downloadError);
throw downloadError;
}
const downloadedBytes = new Uint8Array(await downloaded.arrayBuffer());
console.log('Downloaded bytes:', downloadedBytes.byteLength);
For a public bucket, use the URL returned by getPublicUrl for the same bucket and object path, then inspect the HTTP status and response body. A browser error can mean that the URL points to the wrong object or that the access method does not match the bucket’s visibility—not necessarily that the PDF bytes are damaged.
5. Compare the original and retrieved bytes
Compare lengths first, then compare cryptographic hashes or the byte sequences themselves. Matching lengths alone do not prove identity; matching hashes are a practical check that the retrieved content matches the generated input.
import { createHash } from 'node:crypto';
function sha256(bytes) {
return createHash('sha256').update(bytes).digest('hex');
}
console.log({
generatedLength: pdfBytes.byteLength,
downloadedLength: downloadedBytes.byteLength,
generatedSha256: sha256(pdfBytes),
downloadedSha256: sha256(downloadedBytes),
});
If lengths or hashes differ, check for a conversion at upload, a wrong bucket/path, an overwrite, cache behavior, or an unexpected retrieval response. Confirm the response status before treating its body as a PDF: an error page or other response content is not the object you intended to inspect. If bytes match but viewers still reject the document, reopen the locally generated artifact and investigate PDF generation rather than changing only response headers. A correct Content-Type is useful metadata, not a repair for invalid bytes.
Best Value
- Easily store and access 2TB to content on the go with the Seagate Portable Drive, a USB external hard drive
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
6. Check Puppeteer and browser versions if local output is broken
When the local artifact itself is unreadable, reduce the problem to a minimal HTML page and reproduce PDF generation before changing dependencies. Record the installed Puppeteer package version and the Chrome/Chromium version it launches; then compare the locally generated result across the relevant environment changes.
A Puppeteer issue report describes a test PDF that worked with version 22.15.0 and became unreadable after an upgrade to 23.0.0, in a reported Windows and Node 20.15.0 environment. It is an individual report, not proof of a general regression or the cause of another application’s problem. Treat a version change as a lead to reproduce, not a diagnosis.
Troubleshooting by symptom
| Symptom | Likely distinction | Next check |
|---|---|---|
| The PDF will not open even when saved locally | Failure precedes Supabase. | Confirm page.pdf() completed, inspect page content and readiness, and record Puppeteer/browser versions. |
| Upload returns an error | Storage did not accept the request as sent. | Read the complete error; check MIME validation, bucket/object, permissions, and size limits. |
| Upload succeeds but the downloaded file differs | Upload conversion or retrieval/path issue is possible. | Compare byte lengths and hashes; check object path, overwrite, and response status. |
| Public link fails while the object exists | Access method may not match bucket visibility. | Use a public URL only for public assets; use authorized download or a signed URL for private objects. |
| The file opens but looks wrong or lacks content | Rendering or readiness may be the issue rather than byte corruption. | Check print CSS versus screen CSS, fonts, page dimensions, backgrounds, and when the page is ready. |
Or skip the browser setup
If your actual goal is to capture a website as an image rather than generate a PDF with Puppeteer, ScreenshotNeo is a website screenshot API and MCP server. It is not a replacement for generating a Puppeteer PDF in this workflow; it is an alternative when a website screenshot is the deliverable. Its one-call screenshot example is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for request details. Cookie/consent banners, newsletter popups, and chat widgets are removed before the shot; each step can be turned off. Bot checks, blank pages, failed loads, and cache hits are not billed. An MCP server provides screenshot tools for AI agents. The Free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000 shots. Sign up for the free plan.
Frequently Asked Questions
Does setting `Content-Type: application/pdf` fix a PDF that has already been corrupted?
No. It labels the response or object; compare the generated and retrieved bytes to locate content changes.
Should I assume Puppeteer is at fault if the Supabase URL will not open?
No. First check the local generated file, upload result, object path, and public/private access method.
Can ScreenshotNeo replace Puppeteer for a PDF upload workflow?
No. ScreenshotNeo captures websites as images or PDFs, but this workflow specifically diagnoses Puppeteer-generated PDFs stored in Supabase.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Free tools Windows power users keep installed
One-click scans. No signup required.

