The request or the response
401 Unauthorized from the render API
Every request is rejected, including ones that worked yesterday, or it works locally and fails in deployment.
What is actually happening
The key that arrived is not the key you think you sent. The usual causes are dull and specific: a trailing newline from a file or a secret manager, the word Bearer missing or doubled, or a different environment holding a different value.
Confirming it is this and not something that looks like it
Log the length of the key and its last four characters, never the key itself. Length settles the whitespace question immediately, and the last four settle which key it is.
The fix
Strip whitespace when reading the key, and check the header format. A secret written to a file by an editor picks up a trailing newline, which most HTTP clients will send verbatim.
const key = (process.env.PDFPIPE_KEY ?? "").trim();
if (!key) {
throw new Error("PDFPIPE_KEY is not set in this environment");
}
// Never the key itself, in any log, at any level.
console.log(`using key ending ${key.slice(-4)}, length ${key.length}`);
const headers = { Authorization: `Bearer ${key}` };If that was not it
These produce the same symptom often enough to be worth ruling out before assuming the fix above did not work.
- A trailing newline from a file-based secret or a here-doc
- Bearer included in the stored value and prepended again in code
- A test key used against production, or the reverse
- A revoked or rotated key still cached in a build image
Frequently asked
Does this happen with every rendering engine?
The behaviour behind it is not specific to one tool. The key that arrived is not the key you think you sent. Anything rendering HTML to a paged medium has to make the same decision, so the fix travels with you if you change how the render happens.
Will this show up as an error in my logs?
Yes, this one surfaces as a thrown error or a failed status, which is why it is at least findable. The harder half is that the message often names the call that was in flight rather than the thing that actually failed.
Related failures
Problems people arrive at from the same starting point, or mistake for this one.
413 Payload Too Large when sending HTML to render
The request body exceeded a limit.
The downloaded PDF is corrupted and will not open
The bytes were treated as text somewhere.
The PDF opens in the browser instead of downloading
Content-Disposition is absent or set to inline.
429 Too Many Requests when generating PDFs in bulk
Unbounded concurrency.
The PDF is truncated or ends mid-document
The response was cut off.
CSS for paged documents, and what actually works
Which parts of the paged media specification a browser-based render implements.
Everything that goes wrong, by category
The full list, grouped by where in the pipeline it breaks.
Paste your markup and see the rendered document, without signing up.