invalid_parameter
One parameter in the request has a value the API cannot use, most often a number outside its allowed range. The param field names the parameter, so you rarely need to guess. Fix the value and send again; the same request fails every time. This page is part of the error code reference.
| HTTP status | 400 Bad Request |
error.type |
validation_error |
error.param |
Path of the bad parameter, for example viewport.width |
error.retryable |
false |
| Credits used | 0 |
Example response
{ "error": { "type": "validation_error", "code": "invalid_parameter", "message": "viewport.width must be between 1 and 7680", "param": "viewport.width", "retryable": false, "docs": "https://docs.renderscreenshot.com/errors/invalid_parameter" }, "request_id": "req_abc123" }
param uses the nested JSON path from POST requests. If you sent a flat GET parameter, map it back: width is viewport.width, quality is output.quality, timeout is wait.timeout.
Documented ranges
| Parameter | GET name | Allowed values |
|---|---|---|
viewport.width |
width |
1 to 7680 pixels |
viewport.height |
height |
1 to 4320 pixels |
viewport.scale |
scale |
1 to 3 |
output.quality |
quality |
1 to 100 (JPEG and WebP) |
wait.timeout |
timeout |
Seconds, up to 60 (default 30) |
browser.geolocation.latitude / longitude |
-90 to 90 / -180 to 180 |
Full details are on the viewport, wait and output pages.
More specific codes
Some parameters have their own validation code. When you see one of these, it means the same thing as invalid_parameter, for one known field:
| Code | Parameter |
|---|---|
invalid_format |
output.format |
invalid_url |
url |
invalid_preset |
preset |
missing_required |
A required parameter is missing, such as url |
invalid_cache_ttl |
cache.ttl (3,600 to 2,592,000 seconds) |
invalid_media_type |
browser.media (screen or print) |
invalid_timezone, invalid_locale |
browser.timezone (IANA), browser.locale (BCP 47) |
invalid_paper_format, invalid_pdf_scale, invalid_pdf_margin |
PDF options |
Write your handler for type: validation_error and read param, and every one of these codes is covered.
Common causes
- Values from users. A width or quality typed into your form goes straight to the API.
- Units.
wait.timeoutis in seconds, whilewait.delayis in milliseconds.timeout: 30000is far over the limit. - Retina math. A 2,000 px wide layout with
scale: 4asks for more than the maximum device scale factor of 3. - Old configuration. A value that a previous version of your code clamped is now sent as is.
How to fix it
- Read
error.paramanderror.message. - Check the value against the ranges above, or the parameter's docs page.
- If users set the value, validate it in your own form with the same limits, and show the API message next to the field.
- Never retry a 400 automatically.
This function returns field errors for your form instead of throwing, so the user sees which input to change:
// param-errors.mjs (Node 22+): turn a 400 into a message attached to the right form field. const API = process.env.RS_BASE_URL ?? 'https://api.renderscreenshot.com'; // Map API parameter paths to the fields of your own form. const FIELD_FOR_PARAM = { 'viewport.width': 'width', 'viewport.height': 'height', 'viewport.scale': 'pixelRatio', 'output.quality': 'quality', 'wait.timeout': 'timeout', }; export async function capture(body) { const res = await fetch(`${API}/v1/screenshot`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.RS_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), }); if (res.ok) return { image: Buffer.from(await res.arrayBuffer()) }; const { error, request_id } = await res.json(); if (error.type === 'validation_error') { // Never retry: the same body fails the same way every time. return { fieldErrors: { [FIELD_FOR_PARAM[error.param] ?? 'form']: error.message }, code: error.code, requestId: request_id, }; } throw new Error(`${res.status} ${error.code}: ${error.message} (${request_id})`); } const result = await capture({ url: 'https://example.com', viewport: { width: 9000 } }); console.log(result.fieldErrors ?? `${result.image.length} bytes`);
With the Ruby SDK, every 400 arrives as RenderScreenshot::ValidationError, with the API code in e.code:
require 'renderscreenshot' client = RenderScreenshot.client(ENV.fetch('RS_API_KEY')) begin client.take({ url: 'https://example.com', viewport: { width: 9000 } }) rescue RenderScreenshot::ValidationError => e # 400s are bugs in the request: report them, never retry them. warn "#{e.code}: #{e.message} (#{e.request_id})" end
Tested on 2026-10-15 with Node 22.18, and Ruby 4.0.7 plus the renderscreenshot gem 1.0.0, against a local mock that applies the documented viewport.width range. A width of 9000 produced { width: 'viewport.width must be between 1 and 7680' } in Node and the matching log line in Ruby.
Related
- Error codes for every validation code.
- Error handling and retries for why 400 responses are never retried.