/flags/csharp/dotnet-build-warnaserror
dotnet build -warnaserror / TreatWarningsAsErrors: fail the C# build on warnings
- tool
- C#
- command
- dotnet build
- flag
- -warnaserror[:codes], -p:TreatWarningsAsErrors=true
- default
- off: warnings are printed and the build succeeds
dotnet build -warnaserror makes the build fail when any warning is reported; -warnaserror:CS8602 does it only for the listed codes. The project-file equivalent is <TreatWarningsAsErrors>true</TreatWarningsAsErrors> (or -p:TreatWarningsAsErrors=true on the command line), with <WarningsAsErrors> for specific codes and <NoWarn> to suppress codes entirely. Teams usually set them in CI or in Directory.Build.props so that warnings cannot pile up.
Default, all warnings, one code
dotnet new console -n App -o . >/dev/null 2>&1
cat > Program.cs <<'EOF'
string? name = args.Length > 0 ? args[0] : null;
Console.WriteLine(name.Length);
class Person { public string Name { get; set; } }
EOF
build() { dotnet build --no-incremental "$@" 2>&1 | grep -oE "(warning|error) CS[0-9]+[^[]*" | sort -u; echo "exit: ${PIPESTATUS[0]}"; }
echo "== default"
build
echo "== -warnaserror"
build -warnaserror
echo "== -warnaserror:CS8602"
build -warnaserror:CS8602
echo "== -p:TreatWarningsAsErrors=true -p:NoWarn=CS8618"
build -p:TreatWarningsAsErrors=true -p:NoWarn=CS8618Output
== default warning CS8602: Dereference of a possibly null reference. 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. exit: 0 == -warnaserror error CS8602: Dereference of a possibly null reference. 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. exit: 1 == -warnaserror:CS8602 error CS8602: Dereference of a possibly null reference. 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. exit: 1 == -p:TreatWarningsAsErrors=true -p:NoWarn=CS8618 error CS8602: Dereference of a possibly null reference. exit: 1
By default both nullable warnings are printed and the build succeeds (exit 0). -warnaserror turns both into errors and the build fails with exit 1. With a code list only CS8602 becomes an error and CS8618 stays a warning. NoWarn removes CS8618 entirely, so with TreatWarningsAsErrors only CS8602 fails the build.
The settings
| Setting | Scope | Effect |
|---|---|---|
| dotnet build -warnaserror | MSBuild switch, this build | Every warning (compiler, MSBuild, NuGet) becomes an error |
| dotnet build -warnaserror:CS8602;CS8618 | MSBuild switch, this build | Only the listed codes become errors |
| <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | Project property | All compiler and analyzer warnings become errors |
| <WarningsAsErrors>CS8602;CS8618</WarningsAsErrors> | Project property | Only the listed codes become errors; Nullable is a shortcut for all nullable warnings |
| <WarningsNotAsErrors>CS0618</WarningsNotAsErrors> | Project property | Exceptions to TreatWarningsAsErrors: stay warnings |
| <NoWarn>$(NoWarn);CS1591</NoWarn> | Project property | Suppress the codes completely |
| -warnAsMessage:codes | MSBuild switch | Downgrade the listed codes to messages |
| #pragma warning disable CS8602 | Source file | Suppress in a region of code |
Append to existing lists with $(NoWarn); and $(WarningsAsErrors); so that values from Directory.Build.props or the SDK are not lost.
A typical setup
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- obsolete APIs: report, do not fail -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS0618</WarningsNotAsErrors>
<!-- missing XML comments on public members -->
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
</Project>On a large legacy code base, start with <WarningsAsErrors>Nullable</WarningsAsErrors> or a short list of codes, fix them, and widen the list over time. See Nullable=enable for the nullable warnings themselves.
Pitfalls
- An SDK or analyzer update can add new warnings; with
TreatWarningsAsErrorsthat breaks the build. Pin the SDK withglobal.jsonand the analyzer level with<AnalysisLevel>. - Real compile errors such as CS1061 are errors in any case; these settings only change warnings.
- Incremental builds can skip the compiler after a property change on the command line; add
--no-incrementalwhen trying settings, as the example does. NoWarnhides a warning for everyone. Prefer fixing the code, or a#pragmaon the one line that needs it.
FAQ
What is the difference between -warnaserror and -p:TreatWarningsAsErrors=true?
-warnaserror is an MSBuild switch: it promotes every warning logged during the build, including MSBuild and NuGet warnings, to an error. TreatWarningsAsErrors is a project property passed to the C# compiler and analyzers, so it affects CS and analyzer warnings. In CI both fail the build; the MSBuild switch is broader.
How do I keep one warning as a warning while treating the rest as errors?
Use WarningsNotAsErrors in the project file, for example <WarningsNotAsErrors>CS0618</WarningsNotAsErrors> together with <TreatWarningsAsErrors>true</TreatWarningsAsErrors>. To hide a warning completely, use NoWarn instead.
Why does my build fail on warnings only in Release?
The property is probably inside a PropertyGroup with a Condition on the configuration, for example Condition="'$(Configuration)' == 'Release'". Search the .csproj and Directory.Build.props for TreatWarningsAsErrors.