IronPDF - AccessViolationException After InsertPdf When Applying HTML Headers/Footers (Release Mode)
Overview
In Release builds, InsertPdf(…) replaces a PdfDocument's underlying Pdfium document handle, but the JIT compiler may keep reading the old, now-freed handle from a CPU register instead of from memory. The next native operation — such as AddHtmlHeaders — then passes that stale handle into Pdfium and crashes with an AccessViolationException. Debug builds re-read the handle on every call and are unaffected. The fix is a two-phase flow with a file round-trip: save and dispose the document after inserting attachments, then reload it as a fresh PdfDocument before applying headers and footers.
Environment
- OS: Windows
- Affected Versions: 2026.5.2
- Language/Runtime: .NET — occurs in Release/optimized builds only (
Optimize=true); Debug builds are unaffected - Hosting: Windows Service (production)
Cause
pdf.InsertPdf(attachment, i) makes IronPDF build a new merged Pdfium document and assign it a new internal handle (dstId). With optimizations disabled (Debug, Optimize=false), the JIT reads that updated handle from memory on every call, so later operations like AddHtmlHeaders receive the correct handle. With optimizations enabled (Release, Optimize=true), the JIT is allowed to cache field reads in registers. After InsertPdf, the managed PdfDocument wrapper's handle field is not re-read from memory, so the stale, already-freed dstId is passed into Pdfium's native MergePageObjectsToMultiplePages — which dereferences freed memory and throws the AccessViolationException.
Solution
Split the processing into two explicit phases with a file round-trip in between. This is the recommended approach — reloading the PDF from disk creates a brand-new Pdfium handle, so no stale handle can survive into the header and footer calls.
- Phase 1 — render, add background, insert attachments, then save and dispose. Saving forces Pdfium to commit all state to disk; disposing fully releases the old document handle:
using (var pdf = renderer.RenderHtmlFileAsPdf(inputHtmlFile))
{
pdf.AddBackgroundPdfToPage(0, backgroundPdf);
using (var attachment = new PdfDocument(attachmentFile))
{
pdf.InsertPdf(attachment, insertIndex);
}
// Commit all state to disk, then release the old handle on dispose
pdf.SaveAs(tmpFile);
} // <- old PdfDocument (and its stale handle) is disposed here - Phase 2 — reload the saved file as a fresh
PdfDocument, then apply headers, footers, and bookmarks. The new instance has a valid handle, so the native calls are safe. Save the final file, then delete the temporary one:using (var pdf = new PdfDocument(tmpFile)) // brand-new, valid Pdfium handle
{
var header = new HtmlHeaderFooter { HtmlFragment = headerHtml };
var footer = new HtmlHeaderFooter { HtmlFragment = footerHtml };
pdf.AddHtmlHeaders(header, 1, headerFooterPages);
pdf.AddHtmlFooters(footer, 1, headerFooterPages);
pdf.SaveAs(outputFile);
}
if (File.Exists(tmpFile))
File.Delete(tmpFile);
PdfDocument instance immediately after InsertPdf(…) — that is the call path that exposes the stale handle.