Rotativa has been a popular choice for converting ASP.NET MVC views to PDF documents. Developers appreciate its simple API patterns like ViewAsPdf and ActionAsPdf that integrate naturally with MVC controller actions. However, migrating to .NET Core reveals significant compatibility issues that stem from Rotativa's architecture: it wraps wkhtmltopdf, a command-line tool that has been archived and is no longer maintained.
This article documents the common errors developers encounter when using Rotativa.AspNetCore, explains why these issues persist, and demonstrates how to achieve the same MVC-to-PDF functionality with IronPDF.
The Problem
Rotativa.AspNetCore exists as a port of the original Rotativa library for .NET Core, but it inherits fundamental limitations from its dependency on wkhtmltopdf. Developers encounter a range of errors depending on their deployment environment.
How Rotativa Works
Rotativa acts as a wrapper around wkhtmltopdf, spawning the external executable to render HTML content as PDF. This architecture requires:
- The wkhtmltopdf binary to be present in a specific location
- The application process to have permission to execute external binaries
- Platform-specific binaries (Windows .exe vs Linux/Mac executables)
- All native dependencies that wkhtmltopdf requires
When any of these requirements fail, Rotativa produces errors that can be difficult to diagnose.
The wkhtmltopdf Dependency Problem
The wkhtmltopdf project was archived on January 2, 2023. The repository is now read-only with no further development planned. This matters because:
- No bug fixes or security patches will be released
- The rendering engine uses Qt WebKit, which hasn't been updated since 2012
- Modern HTML5, CSS3, and JavaScript features are not supported
- Known security vulnerabilities remain unpatched
Error Messages and Symptoms
Developers migrating to .NET Core commonly encounter these errors:
Binary Not Found
System.ComponentModel.Win32Exception: The system cannot find the file specified
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at Rotativa.AspNetCore.WkhtmlDriver.Convert(...)
This occurs because the NuGet package does not automatically deploy the wkhtmltopdf executable. Developers must manually copy the binary to the correct location.
Permission Denied on Linux
System.ComponentModel.Win32Exception (13): Permission denied
at Interop.Sys.ForkAndExecProcess(...)
at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo)
at Rotativa.AspNetCore.WkhtmlDriver.Convert(...)
Linux deployments fail when the wkhtmltopdf binary lacks execute permissions or when the application process cannot spawn external executables.
Broken Pipe on Linux
System.IO.IOException: Broken pipe
at System.IO.Pipes.PipeStream.WriteCore(ReadOnlySpan`1 buffer)
at Rotativa.AspNetCore.WkhtmlDriver.Convert(...)
This error indicates communication failure between the .NET application and the wkhtmltopdf process, often due to missing native dependencies.
ArgumentNullException with ActionAsPdf
System.ArgumentNullException: Value cannot be null. (Parameter 'key')
at Rotativa.AspNetCore.ActionAsPdf.CallTheDriver(...)
The ActionAsPdf pattern can fail in .NET Core with null reference errors even when the same code works with ViewAsPdf.
Authentication Loss with ActionAsPdf
When using ActionAsPdf with authenticated controllers, Rotativa often renders the login page instead of the intended view. The wkhtmltopdf process makes a separate HTTP request that does not carry the user's authentication cookies.
Who Is Affected
Deployment Environments
- Linux servers and containers: Most cloud deployments use Linux, where Rotativa requires manual binary installation and permission configuration
- Docker containers: The .NET Core containers from Microsoft do not include wkhtmltopdf or its dependencies
- Azure App Service on Linux: No built-in wkhtmltopdf support
- Windows servers without Visual C++ Redistributable: Requires msvcp120.dll to be manually deployed
Use Cases
- ASP.NET Core applications migrated from .NET Framework
- Projects using
ViewAsPdforActionAsPdfpatterns for invoice generation, report exports, or document creation - Continuous integration pipelines that build and test on Linux
- Containerized microservices requiring PDF generation
Evidence from the Developer Community
The Rotativa.AspNetCore GitHub repository documents persistent issues across multiple years.
Timeline
| Date | Event | Source |
|---|---|---|
| 2018 | Permission denied on Ubuntu reported | GitHub Issue #197{:rel="nofollow"} |
| 2019 | Unable to run under Linux | GitHub Issue #13{:rel="nofollow"} |
| 2020 | Docker deployment questions | GitHub Issue #7{:rel="nofollow"} |
| 2021 | .NET Core 3.1 setup issues | GitHub Issue #79{:rel="nofollow"} |
| 2023-01-02 | wkhtmltopdf repository archived | GitHub{:rel="nofollow"} |
Community Reports
"Rotativa worked perfectly on Windows with .NET Core web API (2.1) and was able to generate PDFs from cshtml files, but when deploying the same project to Ubuntu, it gives permission denied exceptions."
— Developer, GitHub Issues, 2018
The pattern repeats across multiple issues: functionality that works on Windows development machines fails when deployed to Linux production environments.
"Using ViewAsPdf now passing model to send some data and it's working but using ActionAsPdf getting an error: ArgumentNullException: Value cannot be null."
— Developer, Microsoft Forums, 2017
The ActionAsPdf pattern, which was a key feature of Rotativa's MVC integration, exhibits inconsistent behavior in .NET Core that developers must work around by switching to ViewAsPdf.
Root Cause Analysis
Architectural Dependency
Rotativa's architecture creates a process dependency chain:
ASP.NET Core App → Rotativa.AspNetCore → wkhtmltopdf binary → Qt WebKit → libgdiplus (Linux)
Each link in this chain introduces potential failure points. The NuGet package installs only the .NET wrapper; the external binary and its native dependencies must be managed separately.
wkhtmltopdf's Obsolete Rendering Engine
wkhtmltopdf uses a patched version of Qt WebKit that stopped receiving updates in 2012. The Qt project deprecated QtWebKit in 2015 and removed it from Qt 5.6 in 2016. The wkhtmltopdf maintainers considered migrating to QtWebEngine (which uses Chromium) but concluded it was not feasible because certain APIs required by wkhtmltopdf do not exist in the newer engine.
As a result, wkhtmltopdf cannot render:
- CSS Flexbox or Grid layouts
- Modern JavaScript ES6+ features
- Web fonts that require JavaScript loading
- Many HTML5 elements and attributes
Security Vulnerabilities
CVE-2022-35583 documents a critical (CVSS 9.8) Server-Side Request Forgery vulnerability in wkhtmltopdf 0.12.6. Because the project is archived, this vulnerability will not be patched. The wkhtmltopdf maintainers stated this is an application-level concern, but the practical effect is that any application using wkhtmltopdf must implement its own input sanitization to prevent SSRF attacks.
The archived status means:
- No security patches for future CVEs
- No fixes for rendering bugs
- No updates for newer operating systems
- No support for modern web standards
Attempted Workarounds
Workaround 1: Manual Binary Deployment
Approach: Copy wkhtmltopdf executables to the project folder and set "Copy Always" in file properties.
Project/
├── Rotativa/
│ ├── Windows/
│ │ └── wkhtmltopdf.exe
│ └── Linux/
│ └── wkhtmltopdf
└── Program.cs
// In Program.cs or Startup.cs
RotativaConfiguration.Setup(env.WebRootPath, "Rotativa");
Limitations:
- Increases deployment package size significantly
- Requires maintaining platform-specific binaries
- Linux binaries need execute permissions set post-deployment
- Does not solve the underlying technology obsolescence
Workaround 2: Docker Layer Installation
Approach: Install wkhtmltopdf in the Docker image during build.
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
RUN apt-get update && apt-get install -y \
wkhtmltopdf \
libgdiplus \
libc6-dev \
&& rm -rf /var/lib/apt/lists/*
Limitations:
- Adds container build time and image size
- Version of wkhtmltopdf from package managers may differ from expected
- Still using archived, vulnerable software
- libgdiplus introduces its own compatibility issues
Workaround 3: Replace ActionAsPdf with ViewAsPdf
Approach: Avoid the ActionAsPdf pattern that makes separate HTTP requests.
// Instead of:
public IActionResult DownloadPdf()
{
return new ActionAsPdf("Invoice", new { id = 123 });
}
// Use:
public IActionResult DownloadPdf()
{
var model = GetInvoiceModel(123);
return new ViewAsPdf("Invoice", model);
}
Limitations:
- Requires refactoring existing code
- Loses the ability to generate PDFs from actions with complex setup logic
- Does not solve permission or binary issues
- Still relies on wkhtmltopdf
A Different Approach: IronPDF
IronPDF provides PDF generation capabilities for ASP.NET Core without external binary dependencies. Instead of wrapping a command-line tool, IronPDF embeds a Chromium rendering engine directly in the .NET process.
Why IronPDF Avoids These Issues
The architectural difference eliminates the root causes of Rotativa's problems:
- No external binary: The rendering engine is packaged as .NET assemblies
- No spawn/exec permissions needed: Everything runs in-process
- Cross-platform by design: Same code works on Windows, Linux, and macOS
- Modern rendering: Uses Chromium, supporting current HTML5, CSS3, and JavaScript
- Actively maintained: Regular updates with security patches and new features
Code Example: Replacing ViewAsPdf
The following example shows how to achieve the same result as Rotativa's ViewAsPdf pattern:
using IronPdf;
using Microsoft.AspNetCore.Mvc;
public class InvoiceController : Controller
{
// Rotativa pattern - requires external wkhtmltopdf binary
// public IActionResult DownloadPdfRotativa()
// {
// var model = GetInvoiceModel();
// return new ViewAsPdf("Invoice", model);
// }
// IronPDF pattern - no external dependencies
public IActionResult DownloadPdf()
{
// Create the renderer with Chromium engine
var renderer = new ChromePdfRenderer();
// Configure page settings similar to Rotativa options
renderer.RenderingOptions.MarginTop = 25;
renderer.RenderingOptions.MarginBottom = 25;
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
// Get the invoice model
var model = GetInvoiceModel();
// Render HTML content to PDF
// This HTML would typically come from rendering a Razor view
string htmlContent = GenerateInvoiceHtml(model);
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
// Return as file result - displays in browser
return File(pdf.BinaryData, "application/pdf");
}
// Or to force download with filename:
public IActionResult DownloadPdfAsFile()
{
var renderer = new ChromePdfRenderer();
var model = GetInvoiceModel();
string htmlContent = GenerateInvoiceHtml(model);
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
// Third parameter triggers download instead of inline display
return File(pdf.BinaryData, "application/pdf", "invoice.pdf");
}
private InvoiceModel GetInvoiceModel()
{
// Your existing model retrieval logic
return new InvoiceModel { /* ... */ };
}
private string GenerateInvoiceHtml(InvoiceModel model)
{
// Generate HTML from your template
// This could use Razor rendering or string interpolation
return $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; }}
.invoice-header {{ background: #2c3e50; color: white; padding: 20px; }}
table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }}
th, td {{ border: 1px solid #ddd; padding: 12px; text-align: left; }}
th {{ background-color: #3498db; color: white; }}
</style>
</head>
<body>
<div class='invoice-header'>
<h1>Invoice #{model.InvoiceNumber}</h1>
</div>
<table>
<tr><th>Item</th><th>Quantity</th><th>Price</th></tr>
<!-- Invoice items would be rendered here -->
</table>
</body>
</html>";
}
}
Key points about this approach:
- No wkhtmltopdf binary required
- No platform-specific file deployment
- Works identically on Windows, Linux, and macOS
- Full CSS support including Flexbox and Grid
- JavaScript executes as in a real browser
Rendering Razor Views to PDF
For projects heavily using Razor views, IronPDF provides an extension package that renders views directly:
using IronPdf;
using IronPdf.Extensions.Mvc.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
public class ReportController : Controller
{
private readonly ICompositeViewEngine _viewEngine;
private readonly ITempDataProvider _tempDataProvider;
public ReportController(
ICompositeViewEngine viewEngine,
ITempDataProvider tempDataProvider)
{
_viewEngine = viewEngine;
_tempDataProvider = tempDataProvider;
}
public async Task<IActionResult> GenerateReport(int reportId)
{
var model = await GetReportData(reportId);
// Render the Razor view to HTML string
var htmlContent = await RenderViewToStringAsync("Report", model);
// Convert to PDF
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;
renderer.RenderingOptions.WaitFor.RenderDelay(100);
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
return File(pdf.BinaryData, "application/pdf", $"report-{reportId}.pdf");
}
private async Task<string> RenderViewToStringAsync(string viewName, object model)
{
ViewData.Model = model;
using var writer = new StringWriter();
var viewResult = _viewEngine.FindView(ControllerContext, viewName, false);
var viewContext = new ViewContext(
ControllerContext,
viewResult.View,
ViewData,
TempData,
writer,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return writer.GetStringBuilder().ToString();
}
}
Equivalent to ActionAsPdf
IronPDF can also render URLs, providing functionality similar to Rotativa's ActionAsPdf:
public IActionResult GenerateFromUrl()
{
var renderer = new ChromePdfRenderer();
// Enable JavaScript for dynamic content
renderer.RenderingOptions.EnableJavaScript = true;
renderer.RenderingOptions.WaitFor.RenderDelay(500);
// Render from URL - similar to ActionAsPdf
// Note: For local development, ensure the URL is accessible
var pdf = renderer.RenderUrlAsPdf("https://localhost:5001/Report/View/123");
return File(pdf.BinaryData, "application/pdf");
}
API Reference
For detailed documentation on the methods demonstrated:
Migration Considerations
NuGet Package Changes
Remove Rotativa packages and add IronPDF:
<!-- Remove -->
<PackageReference Include="Rotativa.AspNetCore" Version="*" />
<!-- Add -->
<PackageReference Include="IronPdf" Version="*" />
<!-- Optional: For Razor view rendering -->
<PackageReference Include="IronPdf.Extensions.Mvc.Core" Version="*" />
Configuration Changes
Remove Rotativa setup code:
// Remove from Program.cs or Startup.cs:
// RotativaConfiguration.Setup(env.WebRootPath, "Rotativa");
// IronPDF requires no global setup
// Optionally configure license key:
IronPdf.License.LicenseKey = "your-license-key";
Licensing
IronPDF is commercial software with per-developer licensing. A free trial is available for evaluation. For projects currently using Rotativa (which is MIT licensed), the licensing model represents a change, but must be weighed against:
- Time saved debugging deployment issues
- Security of actively maintained software
- Support for modern web standards
- Cross-platform reliability
API Pattern Mapping
| Rotativa Pattern | IronPDF Equivalent |
|---|---|
ViewAsPdf("ViewName", model) |
Render HTML string with RenderHtmlAsPdf
|
ActionAsPdf("ActionName") |
Use RenderUrlAsPdf or render view to string |
CustomSwitches |
RenderingOptions properties |
PageSize.A4 |
PdfPaperSize.A4 |
PageMargins |
Individual margin properties |
What You Gain
- Elimination of wkhtmltopdf binary deployment
- No platform-specific configuration
- Modern CSS support (Flexbox, Grid, CSS variables)
- JavaScript execution with configurable wait times
- Active security updates
- Technical support
What to Consider
- Commercial licensing required for production use
- Different API patterns require code changes
- Larger assembly size than Rotativa wrapper (Chromium engine is included)
Conclusion
Rotativa's architecture as a wrapper around wkhtmltopdf creates deployment challenges in .NET Core that stem from external binary dependencies and an archived rendering engine. The common errors (binary not found, permission denied, authentication loss) all trace back to this design. With wkhtmltopdf archived and unpatched against known security vulnerabilities, continuing to use Rotativa introduces both technical debt and security risk.
IronPDF provides equivalent functionality through an embedded Chromium engine that requires no external binaries, works consistently across platforms, and supports modern web standards. For projects experiencing Rotativa issues in .NET Core deployments, migration eliminates the root causes rather than adding workarounds.
Written by Jacob Mellor, CTO at Iron Software. He leads technical development at Iron Software and originally built IronPDF.
References
- Rotativa.AspNetCore GitHub Repository{:rel="nofollow"} - Official .NET Core port
- Permission Denied on Ubuntu - Issue #197{:rel="nofollow"} - Linux deployment error
- Unable to Run Under Linux - Issue #13{:rel="nofollow"} - Cross-platform issues
- Docker Deployment - Issue #7{:rel="nofollow"} - Container configuration
- .NET Core 3.1 Setup - Issue #79{:rel="nofollow"} - Configuration changes
- wkhtmltopdf Status Page{:rel="nofollow"} - Official archive announcement
- CVE-2022-35583 - NVD{:rel="nofollow"} - SSRF vulnerability details
- wkhtmltopdf SSRF Issue #5249{:rel="nofollow"} - Security vulnerability discussion
- Qt WebKit Deprecation - Issue #3391{:rel="nofollow"} - Why migration to QtWebEngine was not possible
- ActionAsPdf Authentication Issue #120{:rel="nofollow"} - Login page rendering problem
For comprehensive IronPDF documentation including tutorials, API reference, and code examples, visit ironpdf.com.
Top comments (0)