Error Handling
This example shows how to handle expected PDF errors with rix/pdf.
The example uses the public Rix facade:
#include <rix.hpp>and accesses PDF through:
rix.pdfUse this example when you want to check save errors, print stable error codes, and avoid calling value() before checking a result.
Create the file
mkdir -p ~/rix-pdf-error-example
cd ~/rix-pdf-error-example
touch error_handling.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 error_handling.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run error_handling.cppThe failure is expected because the output path is empty.
What this example does
The example creates a document:
auto doc = rix.pdf.document();It adds visible content:
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"This example intentionally uses an invalid output path.");It intentionally saves to an invalid path:
auto saved = rix.pdf.save(doc, "");Then it handles the error:
if (saved.failed())
{
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(saved.error()),
saved.error().message());
return 0;
}PDF operations return explicit results
rix/pdf does not use exceptions for normal expected failures such as invalid paths, file write failures, or image loading errors.
It returns explicit values that you can check.
There are two common return types:
PdfStatus
PdfResult<T>PdfStatus
PdfStatus is used when an operation only succeeds or fails.
Example:
auto saved = rix.pdf.save(doc, "output.pdf");save returns a status.
Use:
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<T>
PdfResult<T> is used when an operation returns a value on success.
Example:
auto bytes = rix.pdf.write(doc);write returns:
PdfResult<std::string>Use:
bytes.ok()
bytes.failed()
bytes.value()
bytes.error()Only call value() after checking success.
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());Error values
A PDF error contains:
a stable error code
a human-readable messageAccess the error:
const auto &error = saved.error();Convert the code to text:
rix.pdf.error.to_string(error)Read the message:
error.message()Stable error codes
PDF error codes are available through:
rixlib::pdf::PdfErrorCodeCommon codes include:
rixlib::pdf::PdfErrorCode::InvalidInput
rixlib::pdf::PdfErrorCode::InvalidImage
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::UnknownUse these when your application needs different behavior for different failures.
Check a specific error
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("hint:", "use a real output file path");
}Save error pattern
Use this pattern when saving files:
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 file failures explicit.
Write error pattern
Use this pattern when writing PDF 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 you need the PDF bytes in memory.
Use save when you want a file.
Invalid path example
This intentionally fails:
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"Invalid path example");
auto saved = rix.pdf.save(doc, "");
if (saved.failed())
{
const auto &error = saved.error();
rix.debug.eprint(
"expected pdf error:",
rix.pdf.error.to_string(error),
error.message());
if (rix.pdf.error.is(
error,
rixlib::pdf::PdfErrorCode::InvalidInput))
{
rix.debug.eprint("hint:", "the output path cannot be empty");
}
return 0;
}
return 1;
}Run:
vix run error_handling.cppMissing folder example
Saving to a folder that does not exist can fail:
auto saved = rix.pdf.save(doc, "output/report.pdf");Create the folder first:
mkdir -p outputThen save:
auto saved = rix.pdf.save(doc, "output/report.pdf");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("file error:", "cannot open output file");
return 1;
}
rix.debug.eprint(
"pdf error:",
rix.pdf.error.to_string(error),
error.message());
return 1;
}This is useful when your application wants a special message for file path failures.
Image loading error example
Image loading also returns a result.
auto image = rix.pdf.image.load_jpeg("missing.jpg");
if (image.failed())
{
rix.debug.eprint(
"image error:",
rix.pdf.error.to_string(image.error()),
image.error().message());
return 1;
}Only use image.value() after checking success.
page.image_fit(
image.value(),
page.x_left(),
page.y_top() - 200.0F,
200.0F,
160.0F);Unsupported image format example
The first image implementation supports JPEG images.
Loading a PNG through load_jpeg can fail:
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("image error:", "only JPEG images are supported");
return 1;
}
}Complete 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(
"expected image error:",
rix.pdf.error.to_string(error),
error.message());
return 0;
}
return 1;
}Run:
vix run error_handling.cppThe failure is expected if the file does not exist.
Write bytes safely
This example writes PDF bytes and checks the result before reading value():
#include <rix.hpp>
int main()
{
auto doc = rix.pdf.document();
auto &page = doc.add_page();
page.text(
page.x_left(),
page.y_top(),
"PDF bytes example");
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("generated bytes:", bytes.value().size());
return 0;
}Run:
vix run error_handling.cppUse errors in a Vix route
When generating a PDF inside a Vix route, return an HTTP error if 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.header("Content-Disposition", "inline; filename=\"report.pdf\"");
res.send(bytes.value());
});
app.run();
return 0;
}Use write in routes because the PDF should be returned as response bytes.
API error shape
For APIs, a useful error shape is:
{
"ok": false,
"error": "InvalidInput",
"message": "Output PDF path cannot be empty."
}In C++:
res.status(500).json({
"ok", false,
"error", rix.pdf.error.to_string(error),
"message", error.message()});This keeps API responses predictable.
Custom error values
You can create error values 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 application code does not need to create PDF errors manually.
This is mainly useful for adapters, tests, and wrappers.
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 error_handling.cppUse in a Vix project
Create a project:
vix new rix-pdf-error-handling --app
cd rix-pdf-error-handlingAdd Rix:
vix add rix/rix
vix installMake sure vix.app contains:
deps = [
"rix/rix",
]A minimal vix.app can look like this:
name = "rix-pdf-error-handling"
type = "executable"
standard = "c++20"
output_dir = "bin"
sources = [
"src/main.cpp",
]
include_dirs = [
"include",
"src",
]
deps = [
"rix/rix",
]
packages = [
"vix",
]
links = [
"vix::vix",
]Put the example code in:
src/main.cppBuild and run:
vix build
vix runSingle-file usage
For examples, tests, and quick experiments:
vix run error_handling.cppIf needed:
vix install -g rix/rix
vix run error_handling.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, "");
return saved.failed() ? 0 : 1;
}When at least one RIX_ENABLE_* macro is defined, only selected modules are mounted.
If you also want debug output:
#define RIX_ENABLE_PDF
#define RIX_ENABLE_DEBUG
#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;
}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>Example:
#include <rix/pdf.hpp>
int main()
{
auto pdf = rixlib::pdf::module();
auto doc = pdf.document();
auto saved = pdf.save(doc, "");
return saved.failed() ? 0 : 1;
}The examples in this documentation prefer the public facade:
#include <rix.hpp>and:
rix.pdfCommon mistakes
Forgetting to install Rix
If rix.hpp is not found, install Rix first.
For a project:
vix add rix/rix
vix installFor single-file usage:
vix install -g rix/rixPutting Rix in packages
Wrong:
packages = [
"rix/rix",
]Correct:
deps = [
"rix/rix",
]deps is for Vix Registry packages.
packages is for CMake package discovery.
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");Correct:
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;
}Expecting missing folders to be created automatically
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 path:
rix.pdf.save(doc, "output.pdf");Using load_jpeg for PNG files
load_jpeg expects JPEG image data.
This can fail:
rix.pdf.image.load_jpeg("logo.png");Use JPEG input for this helper.
Printing errors without the message
The code is useful:
rix.pdf.error.to_string(error)The message gives more context:
error.message()Use both when printing diagnostics.
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());
}Check images:
auto image = rix.pdf.image.load_jpeg("photo.jpg");
if (image.failed())
{
rix.debug.eprint(
"image error:",
rix.pdf.error.to_string(image.error()),
image.error().message());
}Never call value() before checking success.
For project usage:
vix add rix/rix
vix installand keep:
deps = [
"rix/rix",
]Next step
Continue with package rules.
Next: Package rules