Skip to main content

How to Treat Warnings as Errors in .NET C# Projects (TreatWarningsAsErrors)

· 7 min read
Jagdish Kumawat
Founder @ Dewiride

A warning that doesn't stop the build gets ignored, then joined by another, until nobody reads the build output at all. TreatWarningsAsErrors turns every warning into an error, so a new problem stops the build the day it appears. This guide turns it on for a whole solution, shows the exceptions you'll need, and what it does to NuGet's warnings, not only the compiler's.

What It Does​

Microsoft's compiler options page: "The TreatWarningsAsErrors option treats all warnings as errors." And the effect: "All warning messages are instead reported as errors. The build process halts (no output files are built)."

Prerequisites​

Step 1: See a Warning That Doesn't Stop the Build​

The library has a nullable warning: a string property that starts out null.

src/Demo.Data/Customer.cs
namespace Demo.Data;

public class Customer
{
public string Name { get; set; }
}
Output (dotnet build)
Restore complete (0.4s)
Demo.Data net10.0 succeeded with 1 warning(s) (0.9s) → src/Demo.Data/bin/Debug/net10.0/Demo.Data.dll
/Users/you/projects/props-demo/src/Demo.Data/Customer.cs(5,19): warning CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
Demo.App net10.0 succeeded (0.3s) → src/Demo.App/bin/Debug/net10.0/Demo.App.dll

Build succeeded with 1 warning(s) in 1.7s

"Build succeeded with 1 warning(s)": easy to miss, and nothing stops it from shipping.

Step 2: Turn It On​

Directory.Build.props
<Project>
<!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build -->
<PropertyGroup>
<!-- Stop the build on any warning, so problems get fixed while they're small. -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
Output (dotnet build)
Restore complete (0.4s)
Demo.Data net10.0 failed with 1 error(s) (0.1s)
/Users/you/projects/props-demo/src/Demo.Data/Customer.cs(5,19): error CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Build failed with 1 error(s) in 0.6s

A terminal where dotnet build, with TreatWarningsAsErrors set in Directory.Build.props, fails with error CS8618 in Customer.cs instead of a warning.

Same message, now error, and the build stops. The app wasn't built either: it depends on the library. Fix the code, here with public required string Name { get; set; }, and the build passes. Fixed: error CS8618 explains the choices.

Step 3: Keep Chosen Warnings as Warnings​

Turning it on in an existing codebase can produce dozens of errors at once. WarningsNotAsErrors lists warnings that stay warnings: "If you turn on TreatWarningsAsErrors, you can use WarningsNotAsErrors to list warnings that shouldn't be treated as errors."

Directory.Build.props (PropertyGroup)
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- CS8618 stays a warning while the nullable warnings are being fixed. -->
<WarningsNotAsErrors>CS8618</WarningsNotAsErrors>
Output (dotnet build)
Demo.Data net10.0 succeeded with 1 warning(s) (0.1s) → src/Demo.Data/bin/Debug/net10.0/Demo.Data.dll
/Users/you/projects/props-demo/src/Demo.Data/Customer.cs(5,19): warning CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
Demo.App net10.0 succeeded (0.2s) → src/Demo.App/bin/Debug/net10.0/Demo.App.dll

Build succeeded with 1 warning(s) in 0.7s

A terminal where dotnet build succeeds with one CS8618 warning, because WarningsNotAsErrors lists CS8618 while TreatWarningsAsErrors is on.

Every other warning is still an error. Separate several codes with semicolons, and remove the list as you fix them.

The Opposite: Only Some Warnings as Errors​

WarningsAsErrors does the reverse: it turns only the listed warnings into errors. The docs: "You can also use the WarningsAsErrors to set only some warnings as errors." With TreatWarningsAsErrors off and WarningsAsErrors set to CS8618, the same build failed with error CS8618 again, while any other warning would have stayed a warning. I ran it with command-line properties, which override the file for one build:

Terminal
dotnet build -p:TreatWarningsAsErrors=false -p:WarningsAsErrors=CS8618
Output (the lines that matter)
Demo.Data net10.0 failed with 1 error(s) (0.1s)
/Users/you/projects/props-demo/src/Demo.Data/Customer.cs(5,19): error CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
Build failed with 1 error(s) in 0.6s

Hiding a Warning Completely: NoWarn​

NoWarn "lets you suppress the compiler from displaying one or more warnings". With -p:NoWarn=CS8618, the build said only Build succeeded in 0.8s, with no warning at all. That's the strongest tool and the easiest to misuse: a hidden warning is still a problem in the code. If you add codes in a file, keep the existing ones, as the docs show: <NoWarn>$(NoWarn);CS8618</NoWarn>.

NuGet Warnings Count Too​

The compiler page has an Important note: "TreatWarningsAsErrors only impacts the C# compiler, not any other MSBuild tasks in your csproj file." The NuGet documentation says the opposite for NuGet: "NuGet also honors MSBuild properties to suppress warnings or elevate them to errors", including TreatWarningsAsErrors, which "Treats all NuGet warnings as errors". What I saw matches the NuGet docs. In a solution with the setting on, adding a package version with a known vulnerability failed the restore:

Output (dotnet build)
/Users/you/projects/dewiride-inventory-api/src/Dewiride.Inventory.Api/Dewiride.Inventory.Api.csproj : error NU1903: Warning As Error: Package 'Newtonsoft.Json' 12.0.3 has a known high severity vulnerability, https://github.com/advisories/GHSA-5crp-9r3c-p9vr

Restore failed with 1 error(s) in 1.6s

"Warning As Error" says it: NuGet's audit warning NU1903 became an error. That's usually what you want, since nobody ships a known vulnerability by accident. Fixed: "NU1903: Package has a known high severity vulnerability" covers the fix.

Common Mistakes​

MistakeWhat happensDo this instead
Turning it on and suppressing everything with NoWarnThe warnings are hidden, the problems stayFix them, or list them in WarningsNotAsErrors for now
Setting it in one project's .csproj onlyNew projects don't get itSet it in Directory.Build.props
<NoWarn>CS8618</NoWarn> without $(NoWarn)Overwrites the codes set before it, including the SDK's own<NoWarn>$(NoWarn);CS8618</NoWarn>
Turning it on in a large codebase in one goA wall of errors, then the setting gets revertedStart with WarningsNotAsErrors for the noisy codes

FAQ​

Should it be on in local builds too, or only in CI?​

Everywhere. If only CI fails on warnings, developers find out after they push. Set it in Directory.Build.props, which every build reads.

Does it affect dotnet run and dotnet test?​

Yes: both build the project first, with the same properties.

Conclusion​

<TreatWarningsAsErrors>true</TreatWarningsAsErrors> in Directory.Build.props makes every warning stop the build, for every project, compiler and NuGet warnings alike. Use WarningsNotAsErrors to keep specific codes as warnings while you work through them, WarningsAsErrors to make only specific codes errors, and NoWarn rarely, for warnings that truly don't apply.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.