soli-pdf

Errors & warnings

A render ends in one of three ways: a PDF with no warnings, a PDF with warnings, or an exception. Warnings mean the renderer skipped something and carried on. Exceptions mean there is no PDF.

#Warnings

The renderer reports anything it had to skip, and the gem collects those messages in Soli::PDF.last_warnings:

pdf = Soli::PDF.render(template: template, data: data)
Soli::PDF.last_warnings
# => ["unresolved placeholder: ${invoice.po_number}",
#     "no font covers some glyphs in \"Café Ωmega\""]
Warning starts withCauseFix
unresolved placeholder: ${…}The data has no value at that path. The placeholder renders empty.Check the spelling, or send the key with an empty string.
no font covers some glyphs in "…"A character is missing from every loaded font, so it is dropped.Add a font that covers it with the fonts: option. See Text & paragraphs.
image "…" skipped: …An image could not be loaded or was refused: outside the working directory, not a public address, or (with 2.9.0) any source but a data: URI. The rest of the page renders.See Layout & drawing. A data: URI always works.
barcode or QR messagesThe value doesn't fit the symbology, for example an EAN-13 without 12 digits, or a non-EUR EPC code.Validate the value in Ruby before rendering.
decorated box split across pagesA box with a fill or border overflowed a page, so its background was left out.Keep decorated boxes to content that fits a page.

The list is replaced on every successful render and left alone when a render raises.

Note The messages come from the renderer's standard error, so the strings have BINARY (ASCII-8BIT) encoding. Convert them before you log or display any that may contain accented text: Soli::PDF.last_warnings.map { |w| w.dup.force_encoding(Encoding::UTF_8) }.

#Turning warnings into failures

In tests, a warning usually means a broken template or incomplete data. Fail on it:

def render_strict(**args)
  pdf = Soli::PDF.render(**args)
  raise "PDF warnings: #{Soli::PDF.last_warnings.join('; ')}" if Soli::PDF.last_warnings.any?

  pdf
end

In production, log them and serve the PDF anyway.

#Exceptions

Every exception the gem raises inherits from Soli::PDF::Error, so one rescue catches them all:

begin
  pdf = Soli::PDF.render(template: template, data: data)
rescue Soli::PDF::Error => e
  Rails.logger.error("PDF failed: #{e.class}: #{e.message}")
  raise
end

#Soli::PDF::RenderError

render_pdf ran and exited with an error. There is no PDF. The exception carries:

MethodValue
#messageThe renderer's message, stripped of surrounding whitespace. If the renderer printed nothing, it is render_pdf failed (<status>).
#stderrThe renderer's full standard error.
#statusThe Process::Status of the finished process.

Typical messages:

error: failed to parse JSON: unknown variant `bogus`, expected one of `paragraph`, `move`,
       `image`, `table`, `hr`, `rect`, `line`, `qr`, `barcode`, `ellipse`, `list`, `chart`,
       `repeat`, `if`, `unless`, `page_break`, `columns`, `box`, `at` at line 1 column 27
error: failed to parse JSON: key must be a string at line 1 column 2
error: font error: no usable fonts found in ["fonts", "font"]; provide a font directory …
error: io error: No such file or directory (os error 2)
MessageLikely cause
failed to parse JSON: unknown variant …A misspelled element type. The message lists every valid type.
failed to parse JSON: … elsewhereA String passed as template: or data: that isn't valid JSON, or a field with the wrong type. The line and column refer to the JSON the gem sent.
font error: no usable fonts foundThe binary has no fonts/ directory next to it, and no fonts: were given. This happens with a custom SOLI_PDF_BIN build.
io error: No such file or directoryA path given to stationery: or attachments: doesn't exist relative to the current directory.
pdfa error: …Factur-X output combined with password:, which PDF/A forbids.
version `GLIBC_2.35' not foundThe Linux binary needs glibc 2.35 or later. The system's C library is older, as on RHEL 9 or Debian 11. See Installation.

When the template is a Hash you built, parsing errors point at the generated JSON. To find the element, render JSON.pretty_generate(template) in the playground, or check the type values in your template.

#Soli::PDF::BinaryNotFound

The gem could not find an executable to run:

  • unsupported OS: … or unsupported CPU: …: no release asset exists for this platform. Build render_pdf yourself and set SOLI_PDF_BIN.
  • not executable: /path: SOLI_PDF_BIN or Soli::PDF.binary_path= points to a file that is missing or not executable.

#Soli::PDF::DownloadError

The first-use download failed. There is no partial install: the archive is unpacked into a temporary directory and only moved into place once complete.

  • …: HTTP 404: the release asset doesn't exist for this version or platform.
  • SHA-256 mismatch for …: the archive didn't match its published checksum. The gem refuses it. Retry, and report it if the problem persists.
  • too many redirects fetching …: more than five redirects.
  • network errors (DNS, timeouts, TLS) are wrapped with the URL. The connection timeout is 10 s and the read timeout 120 s.

Avoid all of these in production by fetching the binary at build time. See Installation.

#ArgumentError

Raised before anything runs when the call itself is wrong: neither or both of data: and invoice:, xml: together with invoice:, or an unknown option. These are programming errors, so don't rescue them.

#In a Rails app

class ApplicationController < ActionController::Base
  rescue_from Soli::PDF::RenderError do |error|
    Rails.logger.error("PDF render failed: #{error.stderr}")
    render plain: 'The document could not be generated.', status: :unprocessable_entity
  end
end