Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

An empty byte[] from converter.Convert(doc) usually means DinkToPdf received no HTML, was configured to write to a file, or could not load its native wkhtmltopdf library. Start with a tiny hard-coded document, leave GlobalSettings.Out empty, and log native loading errors before investigating CSS or JavaScript. Then add your real template and external resources one at a time.

What an empty array actually tells you

DinkToPdf is a .NET P/Invoke wrapper around the native libwkhtmltox library. The returned array is not a general-purpose error object: it is the in-memory PDF produced when conversion succeeds and no output file is configured. A zero-length array therefore narrows the investigation to input, output mode, native loading, converter lifetime, and page resources.

One especially direct cause is visible in DinkToPdf’s ObjectSettings.GetContent() implementation: when HtmlContent is null, it returns new byte[0]. A template method that returns null can consequently produce an apparently successful conversion call with no PDF bytes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

1. Prove that the conversion input is real

Validate the generated HTML before building the document

Log the final string, not only the model used to create it. Check its length and a small prefix and suffix, while avoiding sensitive data in production logs.

string html = RenderInvoice(model);

if (string.IsNullOrWhiteSpace(html))
    throw new InvalidOperationException("The PDF HTML is null or empty.");

_logger.LogDebug("PDF HTML length: {Length}; starts: {Start}; ends: {End}",
    html.Length,
    html[..Math.Min(80, html.Length)],
    html[^Math.Min(80, html.Length)..]);

Also verify that the document contains at least one object. An empty Objects collection, or an object with neither a reachable Page nor non-null HtmlContent, is not a meaningful conversion request.

Use a minimal control document

Replace your template temporarily with this known-good input:

var doc = new HtmlToPdfDocument
{
    GlobalSettings =
    {
        PaperSize = PaperKind.A4
    },
    Objects =
    {
        new ObjectSettings
        {
            HtmlContent = "<html><body><h1>Test</h1></body></html>",
            WebSettings = { DefaultEncoding = "utf-8" }
        }
    }
};

byte[] pdf = converter.Convert(doc);
if (pdf.Length == 0)
    throw new InvalidOperationException("DinkToPdf returned zero bytes for the control document.");

If this succeeds, the wrapper, native library, and output mode are basically working. Reintroduce your template, stylesheet, images, and scripts separately. If it fails, do not spend time debugging your HTML yet; continue with output and native-runtime checks.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use either Page or HtmlContent

Page must be a URL or file path that the native engine can reach. HtmlContent must contain the complete in-memory markup. Do not set both to accidental placeholders, and do not let a failed template lookup silently turn the content into null.

2. Make sure DinkToPdf is in byte-array mode

Leave GlobalSettings.Out empty for in-memory output

The DinkToPdf README specifies that an empty Out string causes the result to be saved in a byte array. This is the configuration for an ASP.NET response, queue message, or object-storage upload:

var doc = new HtmlToPdfDocument
{
    GlobalSettings =
    {
        PaperSize = PaperKind.A4,
        Out = string.Empty
    },
    Objects =
    {
        new ObjectSettings { HtmlContent = html }
    }
};

byte[] pdf = converter.Convert(doc);
return File(pdf, "application/pdf", "invoice.pdf");

If you configure Out, inspect the file instead

A non-empty Out tells wkhtmltopdf to write to that path. Check that the directory exists, the runtime identity can write there, and the filename is what you expect. Do not use a file-output configuration as evidence that Convert should also return populated bytes; choose one output mode deliberately.

3. Verify the native wkhtmltopdf runtime

Deploy the library with the published application

DinkToPdf’s README instructs you to copy the native library to the project root. In practice, verify the published output directory, not only the source tree. Windows deployments need the matching libwkhtmltox.dll; Linux deployments need the matching libwkhtmltox.so plus its dependent system libraries.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Match operating system and process architecture

The native binary and the process must agree: a 64-bit application needs a compatible 64-bit library, and likewise for 32-bit. A .NET Framework application can expose architecture or native calling-convention problems during converter initialization. In a container, IIS worker process, service account, or restricted host, also verify that the runtime user can read and execute the native file.

Capture the first native exception

Record the original DllNotFoundException, BadImageFormatException, or initialization error before inspecting PDF length. A Linux load failure for libwkhtmltox is a deployment problem, not an HTML problem. Fix the missing file or dependency, republish, and rerun the control document.

4. Use a singleton synchronized converter in servers

The DinkToPdf README recommends SynchronizedConverter for multithreaded applications and web servers. Register one instance for the application lifetime rather than constructing a native converter for every request:

services.AddSingleton<IConverter>(
    new SynchronizedConverter(new PdfTools()));

Inject IConverter into your service and keep conversion calls serialized through that instance while diagnosing intermittent failures. Creating and disposing native converters per request can produce races, initialization failures, and inconsistent results that look like empty output.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

5. Make page-loading settings match the page

Encoding

Set WebSettings.DefaultEncoding to the encoding your HTML actually uses, normally utf-8. Incorrect encoding can turn a valid template into unreadable text or cause resource parsing failures.

JavaScript-rendered content

wkhtmltopdf can finish before a client-side application inserts its content. Enable JavaScript when needed and use a finite load.jsdelay long enough for the page to render. A delay is not a cure for a broken script; inspect console or converter warnings and test the page outside the PDF process.

Images and stylesheets

Set web.loadImages according to the document’s needs. Confirm that URLs are reachable from the server, not merely from your laptop. For local CSS, fonts, or images, decide explicitly whether local-file access should be enabled with load.blockLocalFileAccess. Keep local access restricted when the HTML can contain untrusted paths.

Failed resources, proxies, and authentication

Use the documented proxy settings when outbound traffic requires one. If a single image or stylesheet is optional, choose the appropriate load.loadErrorHandling behavior: abort, skip, or ignore. Capture the converter’s warning and error callbacks so a failed resource is visible instead of inferred from a zero-length response.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

6. A repeatable diagnostic sequence

  1. Log the final HTML length and reject null or whitespace content.
  2. Build the minimal control document with UTF-8 encoding.
  3. Confirm doc.Objects.Count > 0 and that each object has a valid Page or HtmlContent.
  4. Keep GlobalSettings.Out empty when the caller expects bytes.
  5. Confirm the native library and dependent libraries exist in the published directory.
  6. Check process architecture, native architecture, and runtime-user permissions.
  7. Use one singleton SynchronizedConverter in web or multithreaded code.
  8. Add JavaScript, images, local files, proxy settings, and your application template one dependency at a time.
  9. Capture native warnings and errors before checking pdf.Length.

Common symptoms and fixes

Symptom Likely cause Action
byte[0] with no obvious exception HtmlContent is null or the object has no usable input Log the final HTML, reject null, and test the control document.
A PDF file appears but returned bytes are empty or unused GlobalSettings.Out is configured Clear Out for memory output, or consume the configured file intentionally.
DllNotFoundException on Linux Missing or unloadable libwkhtmltox.so or dependency Deploy the correct native file and install its system dependencies; inspect the first load error.
BadImageFormatException or startup architecture error 32/64-bit mismatch or incompatible native build Align process and library architecture and republish.
Works locally, fails under IIS or a container Different working directory, permissions, or missing published native asset Check the deployed directory and runtime identity’s read/execute permissions.
Intermittent failures under load Multiple native converters being created concurrently Register one singleton SynchronizedConverter.
Blank or incomplete PDF JavaScript, images, encoding, local-file access, proxy, or resource errors Adjust only the required setting, add a finite delay, and inspect warnings.

Performance, reliability, and security considerations

Start with a small HTML document because it separates conversion overhead from application complexity. Keep a single synchronized converter warm in a long-running process, but bound request time at your web-server or job-queue layer so a page waiting on an unreachable resource cannot consume workers indefinitely. Cache or inline stable CSS and images when appropriate, while avoiding unbounded base64 documents that increase memory use.

Treat HTML, URLs, cookies, headers, and local-file permissions as security inputs. Restrict local-file access for untrusted content, avoid forwarding secrets into rendered pages, and use a controlled outbound proxy when the environment requires one. Log document identifiers, lengths, elapsed time, and native errors rather than full customer HTML.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your actual requirement is a clean screenshot or PDF of a web page rather than server-side HTML-to-PDF rendering, ScreenshotNeo makes the capture a single HTTP request. It accepts consent banners like a visitor, removes more than 60 known consent platforms plus newsletter popups and chat widgets before capture, and lets you turn each cleanup step off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; every response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.

For a screenshot, follow the ScreenshotNeo API documentation and run:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also supports PDF output, full-page lazy-image loading, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, paper size and page ranges, custom CSS/JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable-TTL caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; Growth is $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing provides two months free, and every feature is included on every plan. Sign up free for ScreenshotNeo and start with the no-card 1,000-shot allowance.

When migration is worth considering

If native deployment remains unacceptable, compare alternatives on the dimensions that caused your failure: native dependency packaging, supported .NET and operating-system combinations, thread-safety model, JavaScript and CSS fidelity, resource-loading controls, output APIs, and maintenance status. Do not choose a replacement solely because it returns a different type; first establish whether your current problem is null input, file output, or an unloadable native library.

Frequently Asked Questions

Why does a valid-looking HTML page still produce no bytes?

A valid string in your application model is not enough if the final value assigned to the DinkToPdf object becomes null, the object collection is empty, or the selected URL cannot be reached. Log the final object values immediately before conversion.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I set both Page and HtmlContent?

Use the one input route you actually need: Page for a reachable URL or path, HtmlContent for in-memory markup. Supplying neither leaves the converter without meaningful content.

Can I create a new converter for every request?

That pattern is unsuitable for server workloads. Use one application-lifetime SynchronizedConverter and let it serialize native conversion calls.

Does an empty array prove that wkhtmltopdf rendered a blank page?

No. A null HtmlContent value is explicitly converted to a zero-length array, and a native load or output-mode problem can prevent normal rendering altogether.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.