Errors
This page explains how error handling works in rix/pdf.
The examples use the public Rix facade:
#include <rix.hpp>and access PDF through:
rix.pdfrix/pdf uses explicit result and status values instead of throwing exceptions for normal PDF operation failures.
This means operations such as saving, writing, loading images, or handling invalid paths return an object you can check.
Basic error example
Create a file:
mkdir -p ~/rix-pdf-errors
cd ~/rix-pdf-errors
touch errors.cppAdd:
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"This example intentionally uses an invalid output path.");
auto saved = rix.pdf.save(doc, "");
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 0;
}
return 1;
}Run it:
vix run errors.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run errors.cppThe output should show a PDF error because the output path is empty.
Error model
rix/pdf exposes two main error-aware return types:
PdfStatus
PdfResult<T>Use PdfStatus when an operation only succeeds or fails.
Use PdfResult<T> when an operation returns a value on success.
PdfStatus
PdfStatus is used for operations that do not return a value.
Example:
auto saved = rix.pdf.save(doc, "output.pdf");save returns a status because it only needs to tell you whether the file was saved.
A status has:
saved.ok()
saved.failed()
saved.error()Example:
if (saved.failed())
{
const auto &error = saved.error();
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
return 1;
}PdfResult
PdfResult<T> is used for operations that return data.
Example:
auto bytes = rix.pdf.write(doc);write returns PDF bytes on success.
A result has:
bytes.ok()
bytes.failed()
bytes.value()
bytes.error()Use value() only after checking that the result succeeded.
auto bytes = rix.pdf.write(doc);
if (bytes.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(bytes.error()),
bytes.error().message());
return 1;
}
rix.debug.print("bytes:", bytes.value().size());PdfError
Errors are represented by PdfError.
A PDF error contains:
a stable error code
a human-readable messageAccess the error:
const auto &error = saved.error();Read the message:
error.message()Convert the code to a string:
rix.pdf.error.to_string(error)or:
rix.pdf.error.to_string(error.code())Error codes
rix/pdf exposes stable error codes through rixlib::pdf::PdfErrorCode.
Common codes include:
rixlib::pdf::PdfErrorCode::InvalidInput
rixlib::pdf::PdfErrorCode::InvalidState
rixlib::pdf::PdfErrorCode::InvalidPageSize
rixlib::pdf::PdfErrorCode::InvalidMargins
rixlib::pdf::PdfErrorCode::InvalidText
rixlib::pdf::PdfErrorCode::InvalidImage
rixlib::pdf::PdfErrorCode::InvalidTable
rixlib::pdf::PdfErrorCode::UnsupportedImageFormat
rixlib::pdf::PdfErrorCode::FileOpenFailed
rixlib::pdf::PdfErrorCode::FileReadFailed
rixlib::pdf::PdfErrorCode::FileWriteFailed
rixlib::pdf::PdfErrorCode::SerializationFailed
rixlib::pdf::PdfErrorCode::WriterError
rixlib::pdf::PdfErrorCode::UnknownThese codes are useful when your application needs to make programmatic decisions.
Check a specific error code
Use:
rix.pdf.error.is(error, rixlib::pdf::PdfErrorCode::InvalidInput)Example:
if (rix.pdf.error.is(
saved.error(),
rixlib::pdf::PdfErrorCode::InvalidInput))
{
rix.debug.eprint("invalid PDF input");
}You can also use the error directly:
if (saved.error().is(rixlib::pdf::PdfErrorCode::InvalidInput))
{
rix.debug.eprint("invalid PDF input");
}Convert an error code to text
Use:
rix.pdf.error.to_string(rixlib::pdf::PdfErrorCode::InvalidInput)Example:
rix.debug.print(
"code:",
rix.pdf.error.to_string(
rixlib::pdf::PdfErrorCode::InvalidInput));Normal save error pattern
Use this pattern when saving a file:
auto saved = rix.pdf.save(doc, "output.pdf");
if (saved.failed())
{
const auto &error = saved.error();
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
return 1;
}This keeps failures explicit and easy to debug.
Normal write error pattern
Use this pattern when writing bytes:
auto bytes = rix.pdf.write(doc);
if (bytes.failed())
{
const auto &error = bytes.error();
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
return 1;
}
const auto &pdf_data = bytes.value();Use write when the generated PDF should be returned from an HTTP route, stored in another system, or handled manually.
Save error example
This example intentionally saves to an empty path:
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"Invalid save path example");
auto saved = rix.pdf.save(doc, "");
if (saved.failed())
{
const auto &error = saved.error();
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
return 0;
}
return 1;
}Run:
vix run errors.cppThe failure is expected.
Missing folder example
Saving to a nested folder fails if the folder does not exist:
auto saved = rix.pdf.save(doc, "output/report.pdf");Create the folder first:
mkdir -p outputThen run again.
Handle file open errors
auto saved = rix.pdf.save(doc, "missing-folder/report.pdf");
if (saved.failed())
{
const auto &error = saved.error();
if (rix.pdf.error.is(
error,
rixlib::pdf::PdfErrorCode::FileOpenFailed))
{
rix.debug.eprint("cannot open output file");
return 1;
}
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
return 1;
}Use this when the application wants a special message for file path problems.
Handle write errors
A write error can happen when serialization fails.
auto bytes = rix.pdf.write(doc);
if (bytes.failed())
{
const auto &error = bytes.error();
rix.debug.eprint(
"pdf write failed:",
rix.pdf.error.to_string(error),
error.message());
return 1;
}If the result succeeds, the bytes are safe to use:
const auto &data = bytes.value();Image loading errors
Image loading returns PdfResult<Image>.
Example:
auto image = rix.pdf.image.load_jpeg("missing.jpg");
if (image.failed())
{
rix.debug.eprint(
"pdf image error:",
rix.pdf.error.to_string(image.error()),
image.error().message());
return 1;
}Only use image.value() after checking success:
auto &page = doc.add_page();
page.image_fit(
image.value(),
page.x_left(),
page.y_top() - 200.0F,
200.0F,
160.0F);Unsupported image format
The first image implementation supports JPEG images.
If a non-JPEG file is loaded through load_jpeg, the operation can fail with:
rixlib::pdf::PdfErrorCode::UnsupportedImageFormatExample:
auto image = rix.pdf.image.load_jpeg("logo.png");
if (image.failed())
{
if (rix.pdf.error.is(
image.error(),
rixlib::pdf::PdfErrorCode::UnsupportedImageFormat))
{
rix.debug.eprint("only JPEG images are supported here");
return 1;
}
}Full image error example
#include <rix.hpp>
int main()
{
auto image = rix.pdf.image.load_jpeg("missing.jpg");
if (image.failed())
{
const auto &error = image.error();
rix.debug.eprint(
"image error:",
rix.pdf.error.to_string(error),
error.message());
return 0;
}
return 1;
}Run:
vix run errors.cppThe failure is expected if missing.jpg does not exist.
Use errors in an HTTP route
When generating a PDF inside a Vix HTTP route, return an HTTP error if PDF generation fails.
#include <vix.hpp>
#include <rix.hpp>
int main()
{
vix::App app;
app.get("/report.pdf", [](vix::Request &, vix::Response &res) {
auto doc = rix.pdf.document();
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"Hello from rix.pdf");
auto bytes = rix.pdf.write(doc);
if (bytes.failed())
{
res.status(500).json({
"ok", false,
"error", rix.pdf.error.to_string(bytes.error()),
"message", bytes.error().message()});
return;
}
res.header("Content-Type", "application/pdf");
res.send(bytes.value());
});
app.run();
return 0;
}Use write in routes because the PDF should be returned as response bytes.
Error response shape
For APIs, a useful JSON error shape is:
{
"ok": false,
"error": "FileOpenFailed",
"message": "Cannot open PDF file for writing."
}In C++:
res.status(500).json({
"ok", false,
"error", rix.pdf.error.to_string(error),
"message", error.message()});This keeps API errors predictable.
Custom error values
You can create an error value through the facade:
auto error = rix.pdf.error.make(
rixlib::pdf::PdfErrorCode::InvalidInput,
"Custom PDF input error.");You can create a success error value:
auto ok = rix.pdf.error.none();Most applications do not need to create PDF errors manually.
This is mainly useful for adapters, wrappers, tests, and higher-level APIs.
Check whether an error is success
auto ok = rix.pdf.error.none();
if (rix.pdf.error.ok(ok))
{
rix.debug.print("no error");
}Check failure:
auto error = rix.pdf.error.make(
rixlib::pdf::PdfErrorCode::InvalidInput,
"Invalid PDF input.");
if (rix.pdf.error.failed(error))
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
}Complete error-handling example
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
doc.set_title("PDF error handling");
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"This document demonstrates explicit PDF errors.");
auto saved = rix.pdf.save(doc, "");
if (saved.failed())
{
const auto &error = saved.error();
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
if (rix.pdf.error.is(
error,
rixlib::pdf::PdfErrorCode::InvalidInput))
{
rix.debug.eprint("hint:", "use a real output file path");
}
return 0;
}
return 1;
}Run:
vix run errors.cppUse in a Vix project
Create a Vix application:
vix new pdf-errors --app
cd pdf-errorsAdd Rix:
vix add rix/rix
vix installIn vix.app, make sure Rix is listed under deps:
deps = [
"rix/rix",
]A small vix.app can look like this:
name = "pdf-errors"
type = "executable"
standard = "c++20"
output_dir = "bin"
sources = [
"src/main.cpp",
]
include_dirs = [
"include",
"src",
]
deps = [
"rix/rix",
]
packages = [
"vix",
]
links = [
"vix::vix",
]Then use error handling in src/main.cpp:
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto saved = rix.pdf.save(doc, "");
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 0;
}
return 1;
}Build and run:
vix build
vix runSingle-file usage
For small scripts, examples, and experiments:
vix run errors.cppIf Rix is installed globally for single-file usage:
vix install -g rix/rix
vix run errors.cppFor project usage, prefer:
vix add rix/rix
vix installand keep the dependency in vix.app:
deps = [
"rix/rix",
]Use only PDF with the facade
If you want the rix.* facade style but only want PDF mounted, define the feature macro before including rix.hpp:
#define RIX_ENABLE_PDF
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto saved = rix.pdf.save(doc, "");
if (saved.failed())
{
return 0;
}
return 1;
}When at least one RIX_ENABLE_* macro is defined, only selected modules are mounted.
Use the independent package
For independent usage, install:
vix add rix/pdf
vix installIn vix.app:
deps = [
"rix/pdf",
]Then include:
#include <rix/pdf.hpp>Use this style when a project only needs PDF and does not need the full unified Rix facade.
For most application documentation, prefer:
#include <rix.hpp>Common mistakes
Calling value() before checking success
Wrong:
auto bytes = rix.pdf.write(doc);
rix.debug.print(bytes.value().size());Correct:
auto bytes = rix.pdf.write(doc);
if (bytes.failed())
{
return 1;
}
rix.debug.print(bytes.value().size());Ignoring save errors
Wrong:
rix.pdf.save(doc, "output.pdf");Better:
auto saved = rix.pdf.save(doc, "output.pdf");
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 1;
}Saving to a missing folder
This can fail:
rix.pdf.save(doc, "output/report.pdf");Create the folder first:
mkdir -p outputSaving to an empty path
This is invalid:
rix.pdf.save(doc, "");Use a real file path:
rix.pdf.save(doc, "output.pdf");Treating normal failures as exceptions
Normal PDF failures are returned as PdfStatus or PdfResult<T>.
Use:
if (result.failed())
{
// handle error
}Do not rely on exceptions for normal save, write, or image-loading failures.
Confusing deps and packages
For a Vix project, do not put Rix packages in packages.
Wrong:
packages = [
"rix/rix",
]Correct:
deps = [
"rix/rix",
]deps is for Vix Registry packages.
packages is for CMake package discovery.
What you should remember
Check save:
auto saved = rix.pdf.save(doc, "output.pdf");
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
}Check write:
auto bytes = rix.pdf.write(doc);
if (bytes.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(bytes.error()),
bytes.error().message());
}Convert errors to strings:
rix.pdf.error.to_string(error)Check specific error codes:
rix.pdf.error.is(
error,
rixlib::pdf::PdfErrorCode::InvalidInput)Never call value() before checking success.
For a Vix project, install Rix:
vix add rix/rix
vix installand use:
deps = [
"rix/rix",
]Next step
Review the PDF API reference.
Next: API Reference