Skip to content
English
  • There are no suggestions because the search field is empty.

IronPdf on .NET Framework 4.8 Windows Services - FileNotFoundException / StackOverflowException at Startup (packages.config projects)

Overview

In packages.config-style projects on .NET Framework 4.7 and later, MSBuild skips copying a small set of NuGet "framework facade" assemblies to bin\ because it treats them as redundant with the in-box framework versions — even when <Private>True</Private> is set. The build still succeeds, because it resolves against the framework reference assemblies, but at runtime IronPdf's dependency graph requests the specific NuGet-shipped versions named in the App.config binding redirects, and the .NET 4.8 in-box facades do not satisfy them. In a Windows Service host this surfaces as a FileNotFoundException when constructing ChromePdfRenderer or a StackOverflowException when setting the license key; copying the facade DLLs into the output or migrating to PackageReference resolves it.


Environment

  • OS: Windows
  • Hosting: Windows Service host (Console and WinForms hosts usually mask the issue via GAC fallback)
  • Language/Runtime: .NET Framework 4.7 and later, reproduced on 4.8 — packages.config project style

Cause

MSBuild's facade-detection logic prevents these NuGet-supplied assemblies from being copied to bin\: System.ValueTuple, System.Buffers, System.Threading.Tasks.Extensions, System.Text.Json, System.Text.Encodings.Web, and System.Text.Encoding.CodePages. It assumes the in-box .NET 4.8 versions are equivalent, but IronPdf (via Ninject and IronSoftware.Common) requests the exact NuGet versions that the App.config binding redirects point to, and the in-box facades do not satisfy those redirects.

In Console and WinForms hosts the GAC fallback usually hides the mismatch. The Windows Service assembly resolver behaves differently, so the same lookup ends in one of two failures:

  • FileNotFoundException when constructing ChromePdfRenderer.
  • StackOverflowException when setting the license key — Fusion's AssemblyResolve handler recurses on the unsatisfied facade.

Solutions

1. Migrate packages.config to PackageReference — Recommended

This aligns with current .NET Framework tooling guidance and fixes the root cause rather than patching the output.

  1. In Visual Studio, right-click packages.config and choose Migrate packages.config to PackageReference…. PackageReference resolves the full transitive dependency graph via obj\project.assets.json and emits per-asset copy-local decisions, so the facade DLLs the runtime needs are shipped to bin\.
  2. Confirm <AutoGenerateBindingRedirects> is enabled (it already is in this project). It regenerates the redirects against the resolved graph.
  3. Review hand-edited <Reference> items and any custom imports after migration. The VS migration tool is not always perfect on .NET Framework 4.8 projects and may need a small amount of manual cleanup.
  4. Rebuild and verify the facade DLLs are now present in bin\.

2. Keep packages.config and copy the facade DLLs in an AfterBuild target

Use this when migrating is not an option. Add an AfterBuild target to the .csproj that copies the facade DLLs directly, bypassing MSBuild's facade detection:

<Target Name="CopyFacadeAssemblies" AfterTargets="Build">   
   <ItemGroup>
     <FacadeAssemblies Include="..\packages\System.Buffers.4.5.1\lib\netstandard2.0\System.Buffers.dll" />
   <FacadeAssemblies Include="..\packages\System.Text.Encodings.Web.6.0.1\lib\net461\System.Text.Encodings.Web.dll" />
       <FacadeAssemblies Include="..\packages\System.Text.Json.6.0.11\lib\net461\System.Text.Json.dll" />
       <FacadeAssemblies Include="..\packages\System.Text.Encoding.CodePages.5.0.0\lib\net461\System.Text.Encoding.CodePages.dll" />
       <FacadeAssemblies Include="..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll" />
       <FacadeAssemblies Include="..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll" />
</ItemGroup>
<Copy SourceFiles="@(FacadeAssemblies)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true" />
</Target>

Adjust the version numbers in the paths to match the package versions restored in your packages\ folder.


Workarounds That Don't Work

  • Setting <Private>True</Private> (Copy Local) on the facade references — MSBuild's facade detection still skips copying them to bin\.