Skip to main content Link Search Menu Expand Document Toggle dark mode Copy Code (external link)
paperwork.help

Troubleshooting

Diagnose and fix common issues when generating PDF documents with Paperwork.Core.


Learning Objectives

By the end of this article, you’ll be able to:

  • Diagnose common PDF generation errors
  • Use logging to debug issues
  • Fix template and parsing problems
  • Resolve path and resource loading issues
  • Optimize performance

Common Errors and Solutions

Error: “Could not find file”

Problem: Template file not found

System.IO.FileNotFoundException: Could not find file 'template.html'

Causes:

  • Incorrect file path
  • File doesn’t exist
  • Wrong working directory

Solutions:

// ❌ Relative path might not resolve
builder.WithLayoutFile("template.html");

// âś… Use absolute path
string templatePath = Path.Combine(
    AppContext.BaseDirectory,
    "Templates",
    "template.html"
);
builder.WithLayoutFile(templatePath);

// âś… Or use full path
string fullPath = Path.GetFullPath("./Templates/template.html");
builder.WithLayoutFile(fullPath);

// âś… Check if file exists first
if (!File.Exists(templatePath))
{
    throw new FileNotFoundException($"Template not found: {templatePath}");
}

Error: “Document could not be created from the layout”

Problem: The template could not be parsed

303. Document could not be created from the layout, check the syntax and sources

Causes:

  • Missing <html> tag
  • Unclosed tags in an XHTML template (one with the xmlns namespace)
  • Invalid XML, such as an unescaped &

Solutions:

<!-- ❌ Unclosed tag with the XHTML namespace -->
<html xmlns='http://www.w3.org/1999/xhtml'>
<body>
    <p>Content<br></p>
</body>
</html>

<!-- âś… Option 1: Close every tag -->
<html xmlns='http://www.w3.org/1999/xhtml'>
<body>
    <p>Content<br /></p>
</body>
</html>

<!-- âś… Option 2: Remove the namespace, and the template is parsed as HTML5 -->
<html>
<body>
    <p>Content<br></p>
</body>
</html>

The document builder chooses the parser from the <html> element: with the xmlns namespace the template must be valid XML, without it the more forgiving HTML5 parser is used.

Error: “Object reference not set to an instance”

Problem: Null reference exception

System.NullReferenceException: Object reference not set to an instance of an object

Common causes:

// ❌ Builder not created
IDocumentBuilder builder = null;
await builder.SaveAsync("output.pdf"); // NullReferenceException

// ❌ Missing data
builder.WithData("model", data); // data is null - may cause null reference in template

// âś… Check for null
if (data != null)
{
    await factory.NewDocument()
        .WithLayoutFile("template.html")
        .WithData("model", data)
        .SaveAsync("output.pdf");
}

Error: “Access to path is denied”

Problem: No permission to write file

System.UnauthorizedAccessException: Access to the path 'C:\output.pdf' is denied

Solutions:

// âś… Write to user documents folder
string outputPath = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
    "output.pdf"
);

// âś… Ensure directory exists and is writable
string directory = Path.GetDirectoryName(outputPath);
if (!Directory.Exists(directory))
{
    Directory.CreateDirectory(directory);
}

// âś… Check permissions
var fileInfo = new FileInfo(outputPath);
if (fileInfo.Exists && fileInfo.IsReadOnly)
{
    fileInfo.IsReadOnly = false;
}

Logging and Diagnostics

Enable Logging

Set a log level on the document builder, and the log is added as pages at the end of the generated PDF.

using Paperwork.Generation;

await factory.NewDocument()
    .WithLayoutFile("template.html")
    .WithData("model", data)
    .WithLogLevel(PaperworkRequestLogOption.Verbose)
    .SaveAsync("output.pdf");

Remove the log level (or set it to Off) for production output.

Log Levels

Level When to Use
Off No log (the default)
Errors Only errors
Warnings Errors and warnings
Messages Info, warnings, and errors
Verbose Everything (debugging)

See Logging for more on the log output.


Template Debugging

Test Template Validity

BuildAsync reports problems in its result rather than throwing.

public async Task<string> CheckTemplate(IPaperworkFactory factory, string templatePath, object sampleData)
{
    var result = await factory.NewDocument()
        .WithLayoutFile(templatePath)
        .WithData("model", sampleData)
        .BuildAsync();

    if (!result.Success)
        return $"{result.Message} {result.Error}";

    if (result.Document?.Binary == null || result.Document.Binary.Length == 0)
        return "Generated PDF is empty";

    return null; // No problems found
}

Resource Loading Issues

Image Not Found

Problem: Images don’t appear in PDF

<!-- ❌ Relative path might not resolve -->
<img src="./images/logo.png" />

Relative paths in a template loaded with WithLayoutFile are found relative to the template file. For a template given as a string or stream, there is no file to be relative to, so they are found from the application’s working directory.

Debug:

// Check the image exists next to the template
string imagePath = Path.Combine(Path.GetDirectoryName(templatePath), "images", "logo.png");
Console.WriteLine($"Image exists: {File.Exists(imagePath)}");

Solutions:

<!-- âś… Load the template with WithLayoutFile, and keep the path relative to the template -->
<img src="images/logo.png" />

<!-- âś… Use absolute path -->
<img src="C:/Projects/MyApp/images/logo.png" />

<!-- âś… Or embed as base64 -->
<img src="data:image/png;base64,iVBORw0KG..." />

External Stylesheet Not Loading

Problem: External CSS not applied

<!-- ❌ Path issues -->
<link rel="stylesheet" href="styles.css" />

Solutions:

<!-- âś… Use full path -->
<link rel="stylesheet" href="C:/Projects/MyApp/styles/common.css" />

<!-- âś… Or embed styles -->
<head>
    <style>
        /* Styles here */
    </style>
</head>

Or add the stylesheet with the document builder, which reads the file from the path given.

builder.WithStyleFile("Templates/styles/common.css");

Remote Resource Timeout

Problem: Remote images/fonts timeout

<img src="https://example.com/slow-loading-image.jpg" />

Solutions:

// Configure the timeout on the HttpClient used by the factory
var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
using var factory = PaperworkFactory.Create(client).Build();

// Or cache remote resources locally
string localImage = DownloadAndCache("https://example.com/image.jpg");
// Use local path in template

Performance Issues

Slow PDF Generation

Problem: PDF takes too long to generate

Diagnose:

Turn on the log, and it includes the time taken by each stage of generation.

await factory.NewDocument()
    .WithLayoutFile("template.html")
    .WithData("model", GetData())
    .WithLogLevel(PaperworkRequestLogOption.Messages)
    .SaveAsync("output.pdf");

Solutions:

  1. Re-use the factory, and cache template text:
private static readonly ConcurrentDictionary<string, string> _templateCache = new();

public IDocumentBuilder NewFromTemplate(IPaperworkFactory factory, string templatePath)
{
    string html = _templateCache.GetOrAdd(templatePath, File.ReadAllText);

    return factory.NewDocument().WithLayout(html);
}
  1. Optimize images:
<!-- ❌ Large unoptimized images -->
<img src="photo.jpg" />  <!-- 5MB file -->

<!-- âś… Optimized and sized -->
<img src="photo-optimized.jpg" style="width: 300pt;" />  <!-- 200KB -->
  1. Simplify CSS:
/* ❌ Complex selectors */
body div.content div.section div.subsection p.text span.highlight {
    color: red;
}

/* âś… Simple selectors */
.highlight {
    color: red;
}
  1. Specify table widths:
<!-- ❌ Auto-calculate widths (slower) -->
<table>
    <tr>
        <td>Content</td>
        <td>More content</td>
    </tr>
</table>

<!-- âś… Explicit widths (faster) -->
<table>
    <colgroup>
        <col style="width: 50%;" />
        <col style="width: 50%;" />
    </colgroup>
    <tr>
        <td>Content</td>
        <td>More content</td>
    </tr>
</table>

Memory Issues

Problem: Out of memory errors

Solutions:

// âś… Create one factory and dispose of it when finished
using var factory = PaperworkFactory.Create().Build();

// âś… Process in batches
foreach (var batch in items.Chunk(100))
{
    ProcessBatch(batch);
    GC.Collect(); // Force garbage collection between batches
}

// âś… Stream large images
// Instead of loading entire image into memory,
// stream from disk or URL

Data Binding Issues

Expression Not Evaluating

Problem: Data binding expressions show as literal text

<!-- Shows: {{model.name}} instead of actual name -->
<p>{{model.name}}</p>

Causes:

  • Data not passed to document
  • Wrong property name
  • Null data

Debug:

// Check the data being passed
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(data));

Solutions:

// âś… Ensure data is passed
var model = new
{
    name = "John Doe",
    email = "john@example.com"
};
builder.WithData("model", model);

// âś… Verify property names match
// Template uses: {{model.name}}
// Data must have: .name property

Testing Strategies

Unit Test Template Generation

[Test]
public async Task TestInvoiceGeneration()
{
    // Arrange
    var testData = new Invoice
    {
        Number = "INV-001",
        CustomerName = "Test Customer",
        Items = new List<InvoiceItem>
        {
            new InvoiceItem { Description = "Item 1", Amount = 100.00m }
        },
        Total = 100.00m
    };

    // Act
    using var factory = PaperworkFactory.Create().Build();

    byte[] pdfBytes = await factory.NewDocument()
        .WithLayoutFile("./Templates/invoice.html")
        .WithData("model", testData)
        .BuildBytesAsync();

    // Assert
    Assert.IsNotNull(pdfBytes);
    Assert.Greater(pdfBytes.Length, 0);
    Assert.That(pdfBytes[0], Is.EqualTo(0x25)); // PDF signature '%'
}

Integration Test with File Output

[Test]
public async Task TestPdfFileGeneration()
{
    // Arrange
    string outputPath = Path.Combine(Path.GetTempPath(), "test.pdf");

    try
    {
        // Act
        using var factory = PaperworkFactory.Create().Build();

        await factory.NewDocument()
            .WithLayoutFile("template.html")
            .SaveAsync(outputPath);

        // Assert
        Assert.That(File.Exists(outputPath));
        var fileInfo = new FileInfo(outputPath);
        Assert.Greater(fileInfo.Length, 0);

        // Verify PDF signature
        byte[] header = new byte[4];
        using (var fs = File.OpenRead(outputPath))
        {
            fs.Read(header, 0, 4);
        }
        Assert.That(header, Is.EqualTo(new byte[] { 0x25, 0x50, 0x44, 0x46 })); // %PDF
    }
    finally
    {
        // Cleanup
        if (File.Exists(outputPath))
        {
            File.Delete(outputPath);
        }
    }
}

Getting Help

Check Documentation

  1. HTML Element Reference - Supported elements
  2. CSS Property Reference - Supported CSS

Community Resources

Reporting Issues

When reporting an issue, include:

  1. Package versions:
    Console.WriteLine(typeof(Paperwork.PaperworkFactory).Assembly.GetName().Version);
    Console.WriteLine(typeof(Scryber.Components.Document).Assembly.GetName().Version);
    
  2. Minimal reproducible example:
    • Simplest template that demonstrates the issue
    • Minimal C# code
    • Sample data if applicable
  3. Error message and stack trace:
    try
    {
        // Your code
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error: {ex.Message}");
        Console.WriteLine($"Stack: {ex.StackTrace}");
    }
    
  4. Environment:
    • .NET version
    • Operating system
    • Hosting environment (console app, ASP.NET, Azure, etc.)

Quick Troubleshooting Checklist

When encountering issues, check:

  • File paths are correct and files exist
  • Template has valid HTML/XHTML structure
  • XHTML templates (with the xmlns namespace) are valid XML
  • Data is passed to the document with WithData
  • Property names in template match data model
  • Images and resources are accessible
  • The factory is created once and re-used
  • Sufficient permissions to write files
  • Error handling is in place
  • Logging is enabled for debugging

Next Steps

Now that you can troubleshoot issues:

  1. Data Binding - Make documents dynamic
  2. Document Configuration - Logging and error handling
  3. Practical Applications - Real-world examples

Start building dynamic documents → Data Binding & Expressions