Validate .NET configuration on startup

Published: Monday 13 April 2026

// ❌ This works… but don't add this
// in Program.cs
app.MapGet("/Product", () =>
new ProductDto(1, "Watch"));

We show you the correct way to organise Minimal API endpoints using separate endpoint classes → Learn more

You deploy your .NET app to production. It starts successfully. But invalid configuration values cause runtime exceptions.

Learn how to validate configuration on startup, so your app fails fast and never ships broken configuration again.

The option class

This is the options class that we will configure in the API.

// EmailOptions.cs
public class EmailOptions
{
	public string SmtpHost { get; set; } = string.Empty;

	public int SmtpPort { get; set; } = 21;
}
// ConfigureServices.cs
public static class ConfigureServices
{
	extension(IServiceCollection services)
	{
		public IServiceCollection AddConfigurationOptions()
		{
			services.AddOptions<EmailOptions>()
				.BindConfiguration("Email");

			return services;
		}
	}
}
// Program.cs
builder.Services
	.AddConfigurationOptions();

Starting the application in production works fine. The problem appears when running the endpoints.

// IEmailService.cs
public interface IEmailService
{
	void SendEmail();
}
// EmailService.cs
public class EmailService : IEmailService
{
	private readonly EmailOptions _emailOptions;

	public EmailService(IOptionsSnapshot<EmailOptions> emailOptions)
	{
		_emailOptions = emailOptions.Value;
	}

	public void SendEmail()
	{
		var errors = new List<string>();

		if (string.IsNullOrWhiteSpace(_emailOptions.SmtpHost))
		{
			errors.Add("'SmtpHost' is not provided");
		}

		if (errors.Any())
		{
			throw new NullReferenceException(
				"Unable to send email due to invalid configuration. " +
				"Missing the following configuration values:" +
				$"\r\n- {string.Join("\r\n", errors)}");
		}

		// Send email
	}
}
// EmailsEndpoints.cs
public static class EmailsEndpoints
{
	extension(WebApplication app)
	{
		public WebApplication MapEmailsEndpoints()
		{
			var group = app.MapGroup("/api/emails");
			group.MapPost("/send", SendEmail);

			return app;
		}
	}

	public static NoContent SendEmail(IEmailService emailService)
	{
		emailService.SendEmail();

		return TypedResults.NoContent();
	}
}
// Program.cs
app.MapEmailsEndpoints();

When the SendEmail endpoint handler runs, it calls SendEmail in EmailService. If SmtpHost is empty, it throws a NullReferenceException.

And that is where the problem lies.

You can deploy the application to production. It starts successfully and you assume everything is working. Until an endpoint is called and exceptions start being thrown.

Surely there must be a way to validate configuration on startup?

Add data annotations

One option is to add data annotations to the properties in your options classes. In this example, we mark SmtpHost as [Required].

// EmailOptions.cs
public class EmailOptions
{
	[Required]
	public string Host { get; set; } = string.Empty;

	public int Port { get; set; } = 21;
}

However, we also need to add extra configuration when registering the options classes. This is done by calling AddOptionsWithValidateOnStart and ValidateDataAnnotations.

// ConfigureServices.cs
public IServiceCollection AddConfigurationOptions()
{
	services.AddOptionsWithValidateOnStart<EmailOptions>()
		.BindConfiguration("Email")
		.ValidateDataAnnotations();

	return services;
}

Now, when the application starts, it throws an exception if the configuration is missing or invalid, and the app crashes.

fail: Microsoft.Extensions.Hosting.Internal.Host[11]
      Hosting failed to start
      Microsoft.Extensions.Options.OptionsValidationException: DataAnnotation validation failed for 'EmailOptions' members: 'SmtpHost' with the error: 'The SmtpHost field is required.'.

If you are new to validating with data annotations, we cover this in our Minimal APIs for complete beginners course, where you will learn how to validate API requests using this approach.

FluentValidation

Data annotations are one option, but you can also use FluentValidation. This requires additional setup but is much easier to test.

Install NuGet packages

Install the following packages:

Create validator classes

First, create an AbstractOptionsValidator class. This inherits from AbstractValidator (from FluentValidation) and implements IValidateOptions so the options can be validated on startup.

// AbstractOptionsValidator.cs
public abstract class AbstractOptionsValidator<TOptions> :
	AbstractValidator<TOptions>,
	IValidateOptions<TOptions>
		where TOptions : class
{
	public ValidateOptionsResult Validate(string? name, TOptions options)
	{
		var result = this.Validate(options);

		if (!result.IsValid)
		{
			return ValidateOptionsResult.Fail(
				result.Errors.Select(e =>
					$"{options.GetType().Name}: {e.PropertyName} - {e.ErrorMessage}"));
		}

		return ValidateOptionsResult.Success;
	}
}

Next, create a validator for the options class by inheriting from AbstractOptionsValidator.

// EmailOptionsValidator.cs
public class EmailOptionsValidator : AbstractOptionsValidator<EmailOptions>
{
	public EmailOptionsValidator()
	{
		RuleFor(e => e.SmtpHost)
			.NotEmpty();
	}
}

Register the validator class

Register the options validator as a singleton.

// ConfigureServices.cs
public static class ConfigureServices
{
	extension(IServiceCollection services)
	{
		...

		public IServiceCollection AddOptionsValidators()
		{
			services.AddSingleton<IValidateOptions<EmailOptions>, EmailOptionsValidator>();

			return services;
		}
	}
}
// Program.cs
builder.Services
	.AddOptionsValidators();

If you run the application now, it will still start even when the configuration is missing. This is because we have not yet enabled validation on startup.

To fix this, add ValidateOnStart when registering the options.

// ConfigureServices.cs
public IServiceCollection AddConfigurationOptions()
{
	services.AddOptions<EmailOptions>()
		.BindConfiguration("Email")
		.ValidateOnStart();

	return services;
}

Now, when the application starts and the configuration is invalid, the validation error is shown immediately and the app crashes.

fail: Microsoft.Extensions.Hosting.Internal.Host[11]
      Hosting failed to start
      Microsoft.Extensions.Options.OptionsValidationException: EmailOptions: SmtpHost -'Smtp Host' must not be empty.

Watch the video

Watch the video where we walk through how to set up and configure configuration validation on startup using both data annotations and FluentValidation.

Download the code example

If you want to try out this tutorial, you can download the code example which provides an example using data annotations and FluentValidation.