File limits and upload troubleshooting
Understand input limits, recover from file errors, and choose a batch your browser can handle.
Home › Tools › File limits and upload troubleshooting
Start with the limit shown beside your tool's file picker. A file can be below that limit and still need more memory than your browser has available. This page explains the current Image to PDF guards, how to diagnose a rejected file, and how to read our older performance measurements.
Document processing happens in your browser. The selected file is not uploaded to a document-processing server. Your device must hold the input, working data and output while the task runs.
Image to PDF: current input limits
Image to PDF checks the following limits before accepting an image. These are application guards, not promises that every supported file will finish on every device.
| Check | Current guard | What to do if it fails |
|---|---|---|
| Images per PDF | 20 images | Create two smaller PDFs, then use Merge PDF if your device can handle the combined document. |
| Each image | 20 MiB (20,971,520 bytes) | Resize or compress the image before adding it. The uploader labels this guard as 20 MB. |
| Whole batch | 100 MiB (104,857,600 bytes) | Remove images or split the batch. The uploader labels this guard as 100 MB. |
| Decoded image | 40 million pixels per image | Reduce width and height with Image Resizer or an image editor. |
| Input format | JPEG, PNG, WebP, GIF, BMP and browser-supported AVIF | Convert HEIC photos with HEIC Converter first. Empty or damaged images cannot be converted by renaming their extension. |
A 6,000 × 4,000 photo has 24 million pixels. One uncompressed RGBA copy alone takes about 96 million bytes; decoding and PDF creation need additional memory. The same photo may occupy only a few megabytes on disk. That is why pixel dimensions matter as well as file size.
A PDF size target controls the output attempt, not these input guards. When you select a target, the tool tries different image quality and scale settings and reports the result it actually achieved. A target can be missed. Read the result message and inspect the downloaded pages before sharing.
When a file will not upload or convert
- Read the named error. An empty file needs to be exported again from its source. A damaged image should first open successfully in an image viewer. An unsupported format needs conversion, not just a new filename.
- Try one small file. In Image to PDF, add a single ordinary JPEG or PNG. If that works, add the remaining images in smaller groups to find the file or batch size causing trouble. Valid images stay in the queue when another image is rejected.
- Separate a loading problem from a file problem. If the processing library reports a loading failure, restore the connection and use Retry. Libraries must download before the browser can process the document.
- Reduce the working set. Close other heavy tabs, reduce image dimensions or select fewer PDF pages. Keep your originals. Repeatedly submitting the same large batch does not give the tab more memory.
- Check the output. Open the saved PDF, count the pages, inspect their order and zoom in on small text. A successful conversion message does not establish that the chosen compression settings suit your document.
For choosing between resizing and compression, see the file-size reduction guide. If another tool fails, report the tool name, browser, file format, approximate size and exact error. You do not need to send a private document to describe the problem.
Historical performance measurements
These historical results were recorded before the September 2026 PDF engine upgrade. They have not been rerun on the upgraded renderer. Coverage: recorded attempts cover 6 of 34 configured size-ladder routes. Successful outputs and failed attempts are labeled separately; this page does not claim site-wide performance coverage.
The tables below describe one machine and one sweep on August 11, 2026. They are retained as a record of that run, not as current maximum file sizes or a performance guarantee. Use the current tool's own input guidance when selecting files.
How this was measured
- Test dateAugust 11, 2026 (UTC)
- Operating systemUbuntu 24.04.4 LTS (linux 6.18.5-fc-v20, x64)
- CPUIntel(R) Xeon(R) Processor @ 2.80GHz — 4 cores
- Installed memory15.7 GB
- Chromium151.0.7922.34
- Firefox153.0
- WebKit26.5
- Browser modeHeadless, default flags, one fresh browser per run
- Playwright1.62.1
- Node.jsv22.22.2
- Per-run timeout180 seconds
- Library sourceslocal cache (scripts/measure/.engine-cache) — no network during the sweep
- Fixture seed1366773063
The harness drives the real tool pages the way a person does: it loads the page, waits for the processing library to arrive, puts a file into the file input, presses the button, and waits. Completion is detected by wrapping URL.createObjectURL before the page's own script runs, so the moment the tool produces its output blob is the moment the clock stops, and the blob's size is the recorded output size. Tool-specific drivers select the file input and action button; those selectors are part of the harness configuration.
Test files are generated from a fixed seed, so two runs feed the tools byte-identical inputs:
- PDF file size — A 10-page text PDF padded with one raw-RGB image XObject to reach the target size. Rungs: 1, 5, 10, 25, 50, 100, 250, 500 MB.
- PDF page count — Text-only pages with no images, so the variable is page count rather than bytes. Rungs: 10, 100, 500, 1000, 2500 pages.
- Image file size — Square RGB PNG of incompressible noise, stored uncompressed to hit the target size. Rungs: 1, 5, 10, 25, 50, 100 MB.
- Image pixel count — Square RGB PNG of a smooth gradient, so the file stays small and the variable is pixel count. Rungs: 1, 5, 10, 25, 50, 100 MP.
- Pasted or loaded text — ASCII words, one line of twelve per newline. Rungs: 1, 5, 10, 25, 50, 100 MB.
Each tool is walked up its ladder by binary search rather than one rung at a time, which finds the same answer — the largest fixture that finishes — in three or four runs instead of eight. A run that produces neither a result nor an error within 180 seconds is recorded as a timeout and left as a timeout. It is not retried until it passes.
The processing libraries (pdf-lib, pdf.js, Tesseract.js and the rest) are served from a local cache during the sweep instead of from their CDNs, so CDN latency cannot show up inside a processing time. The raw completed results are public in measurements.json. The reproducible harness source is retained in the private project repository and is not publicly downloadable.
Two different ceilings
When a tool stops working there are two quite different reasons, and most people meet the first one:
- The page refuses the file. A tool may check the file size, dimensions or batch count before processing. That guard is a product decision, not a measurement of device capacity. An error such as “exceeds 50 MB” describes a tool guard.
- The browser gives up. Above the guard, the real limits are the JavaScript heap and the way these libraries work. These tool implementations read the whole selected file before processing; that is an implementation choice, not a claim about every PDF.js integration. A browser tab that has to hold the source file, a parsed object graph of it, and the output at the same time is using several times the file's size in RAM.
The tables below report the guard where a tool has one, and what was measured underneath it.
PDF file size: what completed
Largest fixture each tool finished, per engine. “Time” and “What happens above” are the Chromium run — a failed run is shown explicitly rather than assigned a successful file-size limit.
| Tool | Chromium | Firefox | WebKit | Time at that size (Chromium) | Ceiling set by | What happens above |
|---|---|---|---|---|---|---|
| Merge PDF | 50 MB | 50 MB | 50 MB | 2.0 s | page limit | Refused by the page: “exceeds 50MB limit”. |
| Split PDF | 5.0 MB | 50 MB | 50 MB | 1.1 s | too slow | Still running after the 180-second cap, with no error shown. |
| Organize PDF | 50 MB | No fixture completed. Failed: “the tool's action button (#organize-apply-btn) never became enabled, and the page showed no error explaining why”. | 50 MB | 2.2 s | page limit | Refused by the page: “File exceeds the 100MB limit. Split it first, then organize the parts”. |
| Rotate PDF | 50 MB | 50 MB | 50 MB | 441 ms | page limit | Refused by the page: “File exceeds 100MB”. |
| Compress PDF | 100 MB | No fixture completed. Failed: “NetworkError: A network error occurred.”. | 100 MB | 10.7 s | browser or engine | Failed: “File size must be less than 200MB.”. |
PDF page count: what completed
Largest fixture each tool finished, per engine. “Time” and “What happens above” are the Chromium run — a failed run is shown explicitly rather than assigned a successful file-size limit.
| Tool | Chromium | Firefox | WebKit | Time at that size (Chromium) | Ceiling set by | What happens above |
|---|---|---|---|---|---|---|
| Merge PDF | 2,500 pages | 2,500 pages | 2,500 pages | 1.5 s | Not reached in this test | Completed the largest fixture tested — the failure threshold was not established. |
| Split PDF | 2,500 pages | 2,500 pages | 2,500 pages | 2.0 s | Not reached in this test | Completed the largest fixture tested — the failure threshold was not established. |
| Organize PDF | 100 pages | No fixture completed. Failed: “the tool's action button (#organize-apply-btn) never became enabled, and the page showed no error explaining why”. | 100 pages | 4.7 s | browser or engine | Failed: “This PDF has 500 pages — the limit is 300. Split it first, then organize the parts.”. |
| Rotate PDF | 2,500 pages | 2,500 pages | 2,500 pages | 1.2 s | Not reached in this test | Completed the largest fixture tested — the failure threshold was not established. |
| Compress PDF | 2,500 pages | No fixture completed. Failed: “NetworkError: A network error occurred.”. | 1,000 pages | 2 min 42 s | Not reached in this test | Completed the largest fixture tested — the failure threshold was not established. |
Peak memory
Only Chromium exposes performance.memory to a page. Firefox and WebKit do not, so there is no peak-heap figure for them here and none has been carried across from the Chromium column — those measurements simply do not exist.
The last column divides sampled JavaScript heap by input size. It is not total tab RAM: worker memory, native canvas allocations and other browser overhead may be absent. Do not use this ratio to estimate a safe file size.
| Tool | Ladder | At this size | Peak JS heap | Heap ÷ input |
|---|---|---|---|---|
| Merge PDF | PDF file size | 50 MB | 9.5 MB | 0.2× |
| Merge PDF | PDF page count | 2,500 pages | 9.5 MB | 1.1× |
| Split PDF | PDF file size | 5.0 MB | 9.5 MB | 1.9× |
| Split PDF | PDF page count | 2,500 pages | 9.5 MB | 1.1× |
| Organize PDF | PDF file size | 50 MB | 9.5 MB | 0.2× |
| Organize PDF | PDF page count | 100 pages | 9.5 MB | 27.8× |
| Rotate PDF | PDF file size | 50 MB | 9.5 MB | 0.2× |
| Rotate PDF | PDF page count | 2,500 pages | 9.5 MB | 1.1× |
| Compress PDF | PDF file size | 100 MB | 9.5 MB | 0.1× |
| Compress PDF | PDF page count | 2,500 pages | 9.5 MB | 1.1× |
| PDF to Word | PDF file size | 50 MB | 9.5 MB | 0.2× |
Why the ceiling is where it is
The heap, not the disk. A browser tab gets a JavaScript heap of a few gigabytes at most, and on mobile far less. A 100 MB PDF is not 100 MB of memory once it is open: pdf-lib holds the original bytes, then a parsed object for every page, font and image inside it, then builds the output document alongside the input. The overhead depends on document structure. The table reports sampled JavaScript heap only; it does not measure total process memory.
WebAssembly memory is its own budget. The tools built on WebAssembly — the PDF encryption pair, and Tesseract for OCR — get a linear memory that grows in 64 KB pages and, on a 32-bit WASM build, cannot exceed 4 GB no matter how much RAM the machine has. Memory growth can fail when the requested allocation cannot be supplied. This dataset does not measure total WebAssembly memory.
How the file is loaded matters. The tested QuickMerge integrations read selected files into memory before processing. This describes our integration, not every possible PDF.js configuration. Holding full input and output buffers can cause a sharp failure when an allocation exceeds the tab's available memory.
Rasterising changes the arithmetic. Three tools — compress, redact, and PDF to image — render each page to a canvas before writing it back out. A canvas costs four bytes per pixel regardless of how simple the page is, so a full-page render at print resolution is tens of megabytes of memory per page, and page count matters more than file size.
The main thread is doing the work. Some processing steps run on the page's main thread; other steps use workers. A long synchronous step can delay repainting and input handling. Total processing time in the historical tables is not a measurement of how long the interface was unresponsive.
What this page is not
The caveats here are not fine print; they are the reason the numbers are worth anything.
- One machine. Intel(R) Xeon(R) Processor @ 2.80GHz, 4 cores, 15.7 GB of RAM, on Ubuntu 24.04.4 LTS (linux 6.18.5-fc-v20, x64). Performance on other devices has not been established by this sweep.
- One run per combination. These are single measurements, not averages over repeated trials, and there are no error bars. This dataset cannot establish whether a difference between engines is repeatable.
- Headless browsers. Headless Chromium, Firefox and WebKit are not identical to the browsers on your desk, particularly around GPU-backed canvas work.
- Synthetic files. The fixtures are generated, not collected. A real PDF containing embedded fonts, images and vector art may behave differently from a generated file of the same size.
- WebKit is not iPhone Safari. The WebKit column is Playwright's Linux WebKit build. Safari on iOS has a different JavaScript engine configuration and much tighter per-tab memory limits, and is outside this dataset. Do not read the WebKit column as an iPhone result.
- Binary search assumes monotonicity. The search takes it for granted that if a tool handles a given size it also handles every smaller one. That is true of everything observed here, but it is an assumption the method makes.
The public measurement dataset contains the completed records and fixed fixture seed. The harness source is not public, so this page does not claim that an outside visitor can reproduce the run from the repository.
For the completed cross-engine subset, see the measured browser-support results.