Binding a configuration section to an options class does not prove that the required values exist or make sense. Without validation, the application can start normally and fail only when the first request tries to create a client or access IOptions<T>.Value.
Register validation rules with the options binding and call ValidateOnStart():
public sealed class AiOptions
{
public const string SectionName = "AI";
public string Endpoint { get; set; } = string.Empty;
public string ChatDeployment { get; set; } = string.Empty;
}
builder.Services
.AddOptions<AiOptions>()
.Bind(builder.Configuration.GetSection(AiOptions.SectionName))
.Validate(
options => !string.IsNullOrWhiteSpace(options.Endpoint)
&& Uri.TryCreate(
options.Endpoint,
UriKind.Absolute,
out var uri)
&& (uri.Scheme == Uri.UriSchemeHttp
|| uri.Scheme == Uri.UriSchemeHttps),
"AI:Endpoint must be an absolute HTTP or HTTPS URI.")
.Validate(
options => !string.IsNullOrWhiteSpace(options.ChatDeployment),
"AI:ChatDeployment is required.")
.ValidateOnStart();
If the entire AI section is missing, binding can still create an AiOptions object with its default empty strings. The validators make those defaults invalid, so host startup fails with an options validation error. The configuration problem becomes visible during deployment or process startup instead of through a user request.
Use data annotations with ValidateDataAnnotations() for simple property rules. The extension is shipped in the Microsoft.Extensions.Options.DataAnnotations package and exposed from the Microsoft.Extensions.DependencyInjection namespace. Some project types require an explicit package reference or using directive. Use IValidateOptions<T> when validation needs its own class or cross-property checks. Keep options validation local and deterministic; do not call external dependencies from it.
ValidateOnStart() does not prove that the endpoint is reachable, the credential is authorized, or the configured deployment exists. Use a deliberate readiness check when the application must verify an external dependency, and keep that check cheap enough to run operationally.
Fail startup when invalid configuration makes the whole application or worker unusable. For an optional feature, validate at that feature boundary and expose an explicit degraded state instead of stopping unrelated parts of the application.