Skip to main content

invalid_request

HTTP 400 · code: "invalid_request" · type: https://docs.formfeed.dev/errors/invalid-request

The request could not be accepted as it is: the body is not JSON, a field has the wrong type or value, or fields are combined in a way the API does not allow. When the body or the query does not fit the contract, issues lists every problem, each with the field (path) and what is wrong (message). For a combination of fields, detail explains it and field names the field.

Example​

{
"type": "https://docs.formfeed.dev/errors/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The request body is invalid",
"code": "invalid_request",
"issues": [
{
"path": "mode",
"message": "Invalid option: expected one of \"sync\"|\"async\""
}
],
"request_id": "req_4b7c1e9a2f3d4c5b8a6e7f9012"
}

In a batch, detail names the item (Item 3 is invalid) and index is its position, counted from 0. Nothing of the batch is queued.

Common causes​

  • The body is not a JSON object, for example because it is empty or cut off. issues then has one entry with an empty path.
  • A render names no source, or more than one: send exactly one of template, html or url.
  • A value outside its list, such as "mode": "later" or "output": "gif", or a field of the wrong type.
  • An output the source cannot make: a PDF or image template, html and url make pdf, png, jpg or webp; a Word template docx or pdf; a PowerPoint template pptx or pdf (field: "output", with the allowed values).
  • version is not published, a channel name, latest or a version number, or it is latest with a live key. Only a test key renders the latest draft.
  • A url or webhook_url that the static address rules refuse: not a valid URL, a scheme other than https (a url may also be http), a user name or password in the URL, or a host that is private, loopback, link-local, a cloud metadata address, localhost, a numeric shorthand such as 2130706433, or a name under .internal, .local, .localhost or .fly.dev. The message starts with URL rejected:. A name that only resolves to a private address is refused later, with navigation_failed.
  • webhook_secret without webhook_url, or shorter than 16 characters.
  • validate_data: true for html or a url, or for a template version without a stored data schema (field: "validate_data"). See Check the data first.
  • post (merge, watermark, password) on an output other than PDF, a watermark with both text and image or neither, or merge_after sources stored in another region than the render.
  • An e-invoice asked for on an image, or together with a password (field: "post.password"): PDF/A forbids encryption.
  • pdf.ua with a value other than true, false or { "check": "strict" | "report" }, on an image, on a Word or PowerPoint template, or together with post.merge_after.
  • A query parameter out of range, such as limit=500 on GET /v1/renders (1 to 100).
  • A template whose shared partials add up to more than 2 MB in one render (partials, bytes, limit).
  • A shared partial name that is not 1 to 64 lowercase letters, digits, dashes and underscores starting and ending with a letter or digit, or a new shared partial when the organisation already holds 200 (feature: "partials").
  • An upload that is empty, has no file part, or is not a type the file library takes.
  • A watermark text with characters the watermark font cannot draw. The font covers Latin, Greek and Cyrillic.

Most of these are answered before anything is recorded. The last one, and a few others the render service finds itself, are found after the render was recorded: then the problem carries render_id, and the render log shows a failed render that cost nothing.

How to fix it​

  • Read detail and the issues array: each issue names the field (path) and what is wrong with it. In a batch, index says which item.
  • Compare the request with the API reference. The SDKs type every field, so most of these mistakes do not compile.

Retry?​

No. The same request gets the same answer. Fix what the problem names, then send the request again with a new Idempotency-Key or without one: a 4xx answer is stored under its key for 24 hours, so the old key returns this problem again. See retrying.