Why Do LLM Gateways Need Image Limits?
Image limits on an LLM gateway: how to check bytes, pixels, PDFs, and attachment count before sending a request to a provider.

A heavy multimodal request should not be treated as a provider error. If a gateway accepts an image or PDF, it is already responsible for distinguishing valid input from a file that could consume worker memory, inflate JSON with base64, exceed the model context, or end in someone else's unclear error.
A limit policy is not meant to punish a client for sending a large scan. It ensures that rejection happens before the external call, uses one stable code, and explains exactly what the client needs to fix. Pixels, bytes, attachment count, and PDF pages measure different risks. Reducing them to one max_file_size_mb field leaves most of the problem uncontrolled.
Early rejection must happen before routing
The gateway should validate attachments before selecting a model and before making a network request to a provider. At this point, it knows the HTTP body size, transfer method, number of parts, declared MIME type, and, after safe parsing, the actual format, image dimensions, and document page count.
Validation after routing may seem attractive: you can take the selected provider's limits and simply forward the file. In practice, this produces three bad outcomes. First, the same client request receives different errors depending on the route. Second, the gateway spends time signing the request, serializing base64, and making an external call even though the answer was known in advance. Third, the client sees text written for someone reading a specific provider's documentation, not for an integration through a compatible API.
Early validation does not replace provider limits. It separates the gateway's public contract from route-specific limits that may change. After the general check, the router can apply a narrower profile for the selected model. If the model accepts only one file or has a smaller area limit, the gateway should reject the request using the same error format while identifying the limit imposed by the selected route.
The sequence is straightforward:
- Limit the request body at the HTTP level.
- Parse the request structure and collect all media parts.
- Decode attachments into a bounded buffer.
- Read metadata without full rendering where possible.
- Check global and route-specific rules.
- Only then send the request to the provider.
At every step, the gateway should stop at the first confirmed violation. There is no need to unpack every PDF page if the header already says that the request body exceeds the allowed limit. There is no need to calculate the pixels of the twentieth image if the file count limit is ten.
Bytes, pixels, and pages answer different questions
File size measures the load on the network, queues, logging, and temporary storage. Pixel dimensions show how much visual data may need to be decoded and sent to the model. Page count measures the amount of document processing. The number of attachment objects limits parsing overhead, tokens, and operation count.
A common gateway mistake is to allow JPEG files up to 10 MB and consider the problem solved. A JPEG with a large uniform area may take little space but produce tens of millions of pixels after decoding. A PNG with transparency, a palette, and metadata creates a different load profile. With PDFs, file size is especially poor at predicting page count, the number of embedded rasters, and rendering complexity.
For images, store at least four values:
encoded_bytes, the number of bytes after base64 decoding or file upload;widthandheight, obtained from the actual format;pixel_count, the product of width and height;mime_type, determined from the content rather than the extension.
For PDFs, add page_count and, if the gateway renders pages itself, limits on the area of each page and the total area. A document with two hundred small pages and one with two hundred A0 posters are not equivalent, even if both have page_count = 200.
Do not combine the pixel limit with the side-length limit. A rule that limits only width or height allows extremely elongated images through. A rule that limits only area allows an image with an absurdly long side that could break a library, consumer, or preview. You need all three rules: maximum width, maximum height, and maximum area.
Base64 increases request size but does not replace file validation
When a client sends a data: URI or a base64 field, the gateway receives a string rather than a ready file. Its length is approximately one-third greater than the original byte count, plus JSON escaping and the URI's service data. The HTTP body limit protects the server from an excessively large request, but does not tell you what the decoded result will be.
Validation therefore needs two thresholds. The first limits Content-Length and the actual number of bytes read when the client uses chunked transfer. The second limits the number of bytes the decoder is allowed to produce. Never decode a string into memory unconditionally and inspect the result size afterward. At that point, the protection has already failed.
The validation pseudocode looks like this:
if request_body_bytes > limits.max_request_bytes:
reject("REQUEST_TOO_LARGE")
for part in media_parts:
if part.base64_chars > limits.max_base64_chars:
reject("MEDIA_ENCODED_SIZE_EXCEEDED", part.index)
decoded = decode_base64_with_output_cap(
part.data,
limits.max_file_bytes
)
if decoded.output_limit_reached:
reject("MEDIA_FILE_SIZE_EXCEEDED", part.index)
The base64 length limit is not a substitute for max_file_bytes. It helps stop processing even earlier and provides a clearer diagnosis when a client has generated a huge string. The output limit protects against incorrect length estimates, spaces, line breaks, URI prefixes, and implementation errors.
URL attachments create a different problem. A gateway should not consider a URL safe simply because the client did not send the bytes directly. If the gateway downloads the object itself, apply limits to the response while reading the stream, block unexpected redirects, and control which destinations outbound requests may reach. Otherwise, the inline base64 limit can be bypassed through a remote file.
Metadata cannot be trusted
A client can label arbitrary bytes as image/png, attach a .jpg extension, or provide plausible width and height values in JSON. None of these fields is suitable for making the decision. The gateway should identify the type from its signature and then safely read the format headers.
For JPEG, dimensions are usually available in frame markers. For PNG, they are in the IHDR header. WebP has its own container structures. This does not mean that a few conditional statements are enough. Formats contain encoding variants and metadata, and image handlers regularly receive security fixes. Use a library that can read headers with resource limits, and update it as carefully as the HTTP stack.
There is an important boundary: reading metadata must not silently turn into full image decoding. If a library allocates a buffer for the entire raster when you only need width and height, you are still vulnerable to an image with an extremely high compression ratio. Set memory and time limits for the handler, not just limits that you check after it finishes.
A useful validation sequence is:
- Compare the declared MIME type with the actual signature and reject conflicts unless the policy allows normalization.
- Allow only the formats you need, not everything the library supports.
- Extract dimensions and the number of frames or pages in a resource-limited process.
- Check width, height, area, and object count.
- Only then decode, transform, or create a thumbnail.
Animated GIF and WebP require a separate decision. If the route accepts only a static visual input, the gateway should explicitly choose the first frame or reject the request with ANIMATED_IMAGE_NOT_SUPPORTED. Silently accepting animation and hoping that every provider interprets it the same way is not safe. Frame count, frame area, and duration create a separate load category that a single-image limit does not cover.
Treat PDFs as both documents and sets of visual pages
A PDF often reaches the gateway as one attachment, even though it may become dozens or hundreds of visual inputs for the model. Some models extract embedded text, some render pages, and some do both. A route should not promise the same quality for every PDF simply because the file passed a megabyte limit.
Gemini documentation explicitly separates this issue: for PDFs, it specifies limits on size and page count, with pages counted as part of document processing. This is a good example of why max_file_bytes cannot be the only rule. Providers change their exact limits and counting methods, but the risk model itself remains the same.
PDF validation should answer three separate questions:
- How many pages does the document contain?
- Can its structure be parsed safely within the allotted memory and time?
- How much work will the selected strategy create: text extraction, page rendering, or both?
A page limit is more useful when combined with a total processed-area limit. For example, a 40-page document may pass the page-count limit but still be a poor candidate for a synchronous request if every page contains a large scan. In that case, the honest options are to ask the client to split the document, reduce the scans before sending them, or route the work through an asynchronous pipeline.
Do not make automatic PDF reduction the only answer. It can erase small text, stamps, tables, and signatures, after which the model may produce a confident but incorrect result. If the gateway offers normalization, the client should enable it explicitly and know which limit will apply. One profile may suit extracting document details, while another suits classifying a multipage package.
One common ceiling does not replace task profiles
Teams often start with a universal rule: up to 20 MB, up to 20 images, and up to 20 megapixels. This is convenient to configure but does not reflect the purpose of the request. A product photo for classification, a passport scan for OCR, a batch of invoices, and a medical image create different requirements for detail, latency, and error tolerance.
Separate public limits into a base profile and explicit processing profiles. The base profile protects the gateway and suits an ordinary chat completion with an image. A document_ocr profile may allow fewer pages but higher resolution per page. A bulk_review profile may allow more objects only in an asynchronous queue. An image_classification profile may force image reduction when the task does not depend on small text.
Do not turn these profiles into hidden router magic. The client should select a mode through a separate parameter, a model alias, or an endpoint. Otherwise, the same request may pass today, be sent to another provider tomorrow, and start losing detail because of an undisclosed resize.
The configuration might look like this:
media_limits:
default:
max_request_bytes: 24MB
max_media_items: 12
max_file_bytes: 8MB
max_width: 8192
max_height: 8192
max_pixels_per_image: 24000000
max_total_pixels: 48000000
max_pdf_pages: 24
document_ocr:
max_request_bytes: 32MB
max_media_items: 6
max_file_bytes: 16MB
max_width: 10000
max_height: 10000
max_pixels_per_image: 40000000
max_total_pixels: 80000000
max_pdf_pages: 12
require_explicit_pdf_mode: true
These numbers are not a universal standard. Derive them from worker capacity, route limits, the actual distribution of client files, and acceptable latency. But the configuration structure matters more than the initial values: it prevents one number from pretending to be a complete policy.
An error should explain the action, not the gateway's internals
The client does not need a provider rejected request string. It needs to know what exceeded the limit, where it happened, and which limit applies. Do not expose the processing stack, internal service names, specific provider rules, or values that could help someone probe the system's capacity.
A good API response is stable, machine-readable, and suitable for a user interface:
{
"error": {
"code": "IMAGE_DIMENSIONS_EXCEEDED",
"message": "Изображение 3 превышает допустимую площадь.",
"param": "messages[0].content[4].image_url",
"details": {
"width": 12000,
"height": 9000,
"pixels": 108000000,
"max_pixels": 24000000,
"max_width": 8192,
"max_height": 8192
},
"request_id": "req_01J..."
}
}
A stable code matters more than the message text. Text changes with localization, while the code is needed by SDKs, retries, and analytics. Use separate codes for an oversized body, an oversized decoded file, too many attachments, image dimensions, total area, page count, unsupported format, and safe parsing failure.
Do not tell the client to simply «try again later». A retry will not fix an image that is 12,000 pixels wide. Instead, specify the action: reduce the long side, export the PDF in parts, send the file through an upload mechanism, choose asynchronous mode, or remove unnecessary attachments.
The gateway should also not automatically retry with another provider after a media-related rejection. If the gateway has already determined that the request violates its own public limit, rerouting will not fix it. If the general limit is satisfied but the route-specific limit is not, a compatible route may be selected only under an explicit policy for preserving quality and controlling cost.
Observability should show causes, not document contents
Without metrics, limits become guesswork. You need to see which checks reject requests most often, which formats create the largest areas, how sizes change after client SDK updates, and how close successful requests come to the limits.
Do not record base64, URLs with signed parameters, PDF contents, or OCR text in ordinary logs for this purpose. Technical fields are enough: input type, actual MIME type, encoded and decoded bytes, width, height, area, page count, rejection code, selected profile, route class, and request identifier. In sensitive industries, even these data points must be covered by your retention and access policies.
Two metrics are especially useful. The first shows the rejection rate for each code and client key. The second shows the distribution of successful requests relative to the limit, such as the share of images above 80% of the permitted area. If a significant share of normal traffic constantly hits the ceiling, do not raise it immediately. First check whether an SDK is sending original camera files instead of resized copies.
Test the boundaries. You need files exactly at the limit and one byte above it, files with maximum width and low height, files with maximum area and valid side lengths, conflicting MIME types, damaged headers, many small images, and PDFs whose declared and actual page counts differ. These tests catch regressions better than one happy-path request with an ordinary JPEG.
Keep route rules separate from public policy
Models differ in supported formats, image count, file transfer method, and the internal cost of visual input. Google Gemini documentation, for example, separately describes limits for inline data, the file mechanism, image count, and PDF pages. These differences cannot be expressed reliably as one number in application code.
Store route capabilities as data: supported MIME types, maximum object count, whether file upload is required instead of inline data, document limits, permitted quality modes, and the date when the record was checked. The gateway's public policy should be the conservative upper layer. Route policy should only narrow it when a specific model requires that.
This separation creates a sensible selection order. First, the gateway filters out input that no supported route would accept. Then it selects models compatible with the format and profile. Finally, it considers price, latency, data residency, and application requirements. If these stages are mixed together, the client's error depends on an arbitrary point in the model-selection logic.
In AI Router, it makes sense to apply this policy at a single OpenAI-compatible entry point, before sending the request to one of the external or locally hosted routes. The client then receives the same contract even when the team changes base_url and continues using its SDK.
Do not try to solve this class of problems with one large number in the configuration. Start with a clear error schema, safe metadata reading, and separate counters for bytes, pixels, objects, and pages. After that, every discussion about a new limit will be about a specific load and result quality, rather than guesswork based on the text of the next provider response.
Frequently asked questions
Do I need to limit every file if the request already has a size limit?
Check each image object separately and the entire request as a whole. Otherwise, a client can bypass the per-file limit by sending dozens of files that look safe individually but create an unmanageable load together.
Can heavy images be controlled with a megabyte limit alone?
No. The byte size describes transfer and storage, but says very little about the cost of decoding and visual processing. A small compressed file may contain an image with a very large area.
Should PDF pages be counted as images?
Usually yes when the PDF is used for visual analysis. It has its own load dimension: the number of pages, with each page potentially becoming a separate image for the model.
How should I choose the image count limit for a single request?
Start with a conservative overall limit, measure real-world distributions, and create a separate route for batch tasks. Do not set a high ceiling just because one provider once accepted a large file.
Why not simply send the file to the provider and show its error to the client?
It is better to return an error before contacting the provider. This reduces latency, avoids unpredictable messages from different providers, and prevents a clearly invalid request from becoming a billable attempt.
How should base64 be accounted for in API limits?
Treat the base64 size as a transport limit, while the decoded file size and pixel count are content limits. The response should make clear which specific limit the client exceeded.
Can I trust the MIME type sent by the client?
Do not trust the file name, extension, or declared MIME type. The gateway should check the format signature, extract metadata with a safe parser, and reject the file if its type does not match the declaration.
Should all images be resized automatically before being sent to the model?
A receipt, form, or page with small text may require higher detail. For general scene classification, that detail often only increases latency and cost, so it is useful to separate task modes.
What should a client do after an IMAGE_DIMENSIONS_EXCEEDED error?
Do not retry automatically without changing the attachments. Resending the same large file only consumes quota and hides the client error. Suggest compression, resizing, or splitting the PDF instead.
How should different limits across models and providers be handled?
When provider rules change, update the route capability table rather than changing the general public contract without a reason. Clients need a stable set of clear error codes, while provider selection should remain the gateway's responsibility.