DEV Community

IronSoftware
IronSoftware

Posted on

Rotativa Not Working in .NET Core: Modern Alternative (Issue Fixed)

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:

  1. The wkhtmltopdf binary to be present in a specific location
  2. The application process to have permission to execute external binaries
  3. Platform-specific binaries (Windows .exe vs Linux/Mac executables)
  4. 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(...)
Enter fullscreen mode Exit fullscreen mode

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(...)
Enter fullscreen mode Exit fullscreen mode

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(...)
Enter fullscreen mode Exit fullscreen mode

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(...)
Enter fullscreen mode Exit fullscreen mode

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 ViewAsPdf or ActionAsPdf patterns 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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
// In Program.cs or Startup.cs
RotativaConfiguration.Setup(env.WebRootPath, "Rotativa");
Enter fullscreen mode Exit fullscreen mode

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/*
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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>";
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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");
}
Enter fullscreen mode Exit fullscreen mode

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="*" />
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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

  1. Rotativa.AspNetCore GitHub Repository{:rel="nofollow"} - Official .NET Core port
  2. Permission Denied on Ubuntu - Issue #197{:rel="nofollow"} - Linux deployment error
  3. Unable to Run Under Linux - Issue #13{:rel="nofollow"} - Cross-platform issues
  4. Docker Deployment - Issue #7{:rel="nofollow"} - Container configuration
  5. .NET Core 3.1 Setup - Issue #79{:rel="nofollow"} - Configuration changes
  6. wkhtmltopdf Status Page{:rel="nofollow"} - Official archive announcement
  7. CVE-2022-35583 - NVD{:rel="nofollow"} - SSRF vulnerability details
  8. wkhtmltopdf SSRF Issue #5249{:rel="nofollow"} - Security vulnerability discussion
  9. Qt WebKit Deprecation - Issue #3391{:rel="nofollow"} - Why migration to QtWebEngine was not possible
  10. 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)