# NullGuard.Fody: null checks woven into the assembly after compilation

> This Fody add-in rewrites your IL so that null arguments throw before your code runs, in one of three modes depending on how your project already declares nullability. The price is that the checks are invisible in your source and imposed after the fact.

**Fody/NullGuard** — Adds null argument checks to an assembly

- Repository: https://github.com/Fody/NullGuard
- Stars: 681 · Forks: 52
- Language: IL Assembly
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/fody-nullguard

## The guards are injected into IL, not written by you

NullGuard is an add-in for Fody, and the Fody model is the whole design. Fody is a tool that lets you write code that runs as part of the build and rewrites the compiled assembly, and NullGuard uses that to add null argument checks to an assembly, which is how the repository describes it.

The consequence is that the checks do not exist in your source code. You write a method with a `string` parameter and nothing else, and the compiled output contains the guard. The README shows the source side:

```csharp
public class Sample
{
    public void SomeMethod(string arg)
    {
        // throws ArgumentNullException if arg is null.
    }

    public void AnotherMethod([AllowNull] string arg)
    {
        // arg may be null here
    }
```

and then shows what the compiler produces for that same class, which is where the real behaviour lives:

```csharp
    public void SomeMethod(string arg)
    {
        if (arg == null)
        {
            throw new ArgumentNullException("arg");
        }
    }
```

That is the trade in one snippet. The guard is uniform and it cannot be forgotten, but a developer reading the source, or a debugger stepping through the original code, does not see it.

The other Fody-specific detail is the installation. Both packages are needed, and the README explains why the Fody package has to be installed explicitly: NuGet always defaults to the oldest, and by its own description the buggiest, version of any dependency.

## Three modes, and the project usually picks for you

NullGuard supports three modes of operation, and if you do not configure one explicitly the tool decides. The auto-detection rules are stated in the README and they are worth knowing, because a project that thinks it is in one mode can end up in another.

If C# 8 nullable attributes are detected, nullable reference types mode is used. Otherwise, if `JetBrains.Annotations` is referenced and `[NotNull]` is used anywhere, the project switches to explicit mode. Failing both tests, the default is implicit mode, which the README describes as how NullGuard has always worked: everything is assumed not-null unless marked with `[AllowNull]`.

Explicit mode is the mirror image. Everything is assumed nullable unless attributed with `[NotNull]`, and the README says this mode is designed to support the R# nullability analysis using pessimistic mode. In that mode, guards are added only for items carrying an explicit `[NotNull]` annotation, and inheritance of nullability is supported, so an implementation of an interface or a base method annotated with `[NotNull]` gets guards on your implementation too.

The practical consequence for a team is that switching mode silently changes which parameters are checked across a whole assembly, and the switch is decided by annotations you may not have added on purpose.

## Return values and properties are guarded too, not just arguments

The compiled output in the README goes beyond method arguments. A method whose return type is not marked as nullable gets a check on the returned value, and properties get checks on the getter and, where the property is not nullable, on the setter.

The setter case is the one that changes behaviour most visibly, because it turns an assignment into a potential exception:

```csharp
        set
        {
            if (value == null)
            {
                throw new ArgumentNullException("value", "Cannot set the value of property 'SomeProperty' to null.");
            }
            someProperty = value;
        }
```

The getter side throws an `InvalidOperationException` with a message naming the property, and the method return value throws one naming the method. The README notes that null checking works for automatic properties too, and the source example shows the attribute applied to a whole property or to just the setter, using `[param: AllowNull]`.

This is the part that decides whether NullGuard is a good fit for your codebase. A guard on arguments is a familiar, conventional thing. A guard on a return value and on a property setter is a stronger contract than most teams write by hand, and it applies to every property in the assembly that is not explicitly marked nullable.

## JetBrains annotations bring a build define with them

Explicit mode has a setup cost that the README spells out in detail. You can use the `[NotNull]` attribute defined in `JetBrains.Annotations` or define your own, and the README notes that not referencing `JetBrains.Annotations` will not auto-detect explicit mode, so you have to set the mode in the configuration yourself.

If you do use the JetBrains attributes, there is a further requirement. Using `JetBrains.Annotations` requires you to define `JETBRAINS_ANNOTATIONS` so the attributes are included in the assembly, otherwise NullGuard cannot find them.

The behaviour in explicit mode is shown by the source sample:

```csharp
    public void SomeMethod([NotNull] string arg)
    {
        // throws ArgumentNullException if arg is null.
    }
```

and for a property, where the annotation goes on the type:

```csharp
    [NotNull]
    public string SomeProperty { get; set; }
```

The README also notes that NullGuard will neither remove those attributes nor the reference to `JetBrains.Annotations`, and points to a JetBrains annotation cleanup step for teams that want them gone. So the annotations stay in your source and in your assembly, and the guards are added on top.

## Adding the two packages is the whole installation

Installation is a NuGet operation, and the README gives the two commands in PowerShell Package Manager form:

```powershell
PM> Install-Package Fody
PM> Install-Package NullGuard.Fody
```

The order in the README is not arbitrary. The text under the block states that the `Install-Package Fody` step is required because NuGet always defaults to the oldest, and by the README's own words the buggiest, version of any dependency. Installing NullGuard.Fody alone would pull in whatever Fody version NuGet resolves for it, which is the outcome the project is warning you about.

Beyond the packages, the rest of the setup is a matter of choosing a mode, and the README points at the general Fody usage documentation for the build integration itself. There is no separate configuration file in the README, so the mode is either set in the project configuration or left to the auto-detection rules described earlier.

For a team already using Fody for other add-ins, the incremental work is one extra package and a decision about mode. For a team not using Fody at all, this is the point to weigh the alternative below, because the weaving approach brings the build system with it.

## The compiler may already be doing this for you

The comparison worth making is with the null checks the C# compiler itself can give you, because NullGuard's NRT mode exists precisely to defer to them. If your project is compiled with nullable reference types enabled, the compiler already warns where a reference may be null, using the same `?` annotations that NullGuard reads in NRT mode to decide what to guard. The difference in approach is the moment of detection: compiler warnings are static and arrive at build time on the line that caused the problem, while woven guards are dynamic and arrive at run time on the parameter that was null.

Static analysis lets the code be wrong in a nullable sense without a runtime cost, and it points at the caller. A woven guard converts the same mistake into an exception with a stack trace, and it applies to every assembly, including ones you do not compile with warnings enabled, which is the argument for it in a library.

The hand-written alternative, a guard clause at the top of each public method, is more explicit and survives without any build tooling, but it is exactly the practice that gets skipped under deadline pressure. NullGuard is the automated version of that discipline, and the README does not claim otherwise, since it presents the tool as a way to have those checks present regardless of what the source says.

## Releases stopped in 2023 while the branch kept moving

The maintenance picture has two parts that need reading together. The repository is not archived and the last push was on 2026-10-16, so there is recent work on the branch. The published versions are much older: 3.1.1 from 2023-01-13, 3.1.0 from 2022-01-14 and 3.0.0 from 2020-07-11, and the README points to the project milestones for release notes rather than to a changelog file.

That gap between a moving branch and a stale tag is the thing to weigh before adopting. If you pin the NuGet package, you get 3.1.1, which predates whatever has changed on the branch in the intervening years. If you consume from source, you are building something whose release notes live in a milestone tracker.

The repository layout shows how the project is organised for verification. There are separate sample and test directories for each mode, with AssemblyToProcess, AssemblyToProcessExplicit and AssemblyToProcessNullableRefTypes as the three subject assemblies, plus AssemblyWithAnnotations and AssemblyWithExternalAnnotations for the JetBrains attribute cases. Tests, TestsCommon, TestsExplicit and TestsNullableRefTypes mirror them, and NetFrameworkSmokeTest with a runTests.ps1 script and a global.json pin suggest the test matrix is driven by a script rather than by hand.

On licensing, the file at the root is license.txt and the repository is identified as MIT. The README also carries an explicit expectation that developers using Fody become a Patron on OpenCollective, which is a request rather than a licence term, and points to a licensing and Patron FAQ for the details.

## Conclusion

NullGuard.Fody fits a .NET team that wants argument null checks applied uniformly across a codebase and cannot rely on every developer remembering them, and that already uses Fody. It is the wrong choice if your code is compiled with nullable reference types and you trust the compiler warnings, or if you need to see the guards in source review, since the woven checks exist only in the compiled output. Verify first which mode the auto-detection picks in your project, because the rules differ on whether JetBrains annotations are referenced, and check the release history, since the newest tagged version is 3.1.1 from 2023-01-13 while the last push was on 2026-10-16.

## FAQ

### What does NullGuard.Fody actually do to my assembly?

It is a Fody add-in that adds null argument checks to an assembly during the build, so guards exist in the compiled IL even when your source has no explicit check. The README shows a plain method with a string parameter compiled into one that throws ArgumentNullException when the argument is null.

### How do I install NullGuard.Fody?

Install both packages with PM> Install-Package Fody and PM> Install-Package NullGuard.Fody. The README states that installing the Fody package is required because NuGet always defaults to the oldest, and by its description the buggiest, version of any dependency.

### What are the three modes of NullGuard and how is the mode chosen?

Implicit mode assumes everything is not-null unless marked with [AllowNull], explicit mode assumes everything is nullable unless marked with [NotNull], and nullable reference types mode uses the C# 8 annotations. Without explicit configuration, the README says C# 8 nullable attributes select NRT mode, referencing JetBrains.Annotations with [NotNull] selects explicit mode, and otherwise implicit mode is the default.

### Does NullGuard.Fody also check return values and properties?

Yes. The compiled output in the README throws InvalidOperationException for a null return value, checks the getter of a non-nullable property, and adds a null check to the setter that throws ArgumentNullException. The README also notes that null checking works for automatic properties.

### What extra setup does JetBrains annotations require in NullGuard?

Using JetBrains.Annotations requires defining JETBRAINS_ANNOTATIONS so the attributes are included in the assembly and NullGuard can find them. The README also notes that not referencing JetBrains.Annotations prevents auto-detection of explicit mode, and that NullGuard neither removes the attributes nor the reference.

## Sources

- [Fody/NullGuard on GitHub](https://github.com/Fody/NullGuard)
- [Issues](https://github.com/Fody/NullGuard/issues)
- [License: MIT](https://github.com/Fody/NullGuard/blob/master/LICENSE)
- [README](https://github.com/Fody/NullGuard/blob/master/README.md)
- [Releases](https://github.com/Fody/NullGuard/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/fody-nullguard
