Skip to content

Diagnostics ​

The generator reports problems in your declarations as diagnostics with ids from VM1001 to VM6001. They appear in the IDE as you type and in the build output. This page lists each one, what causes it, and what to do.

The first digit of the id names the area:

IdsArea
VM1xxxConstraint attributes
VM2xxxDataAnnotations attributes
VM3xxxRules classes
VM4xxxLanguage packs
VM5xxxThe generator and the runtime package
VM6xxxRegistration

Changing a severity ​

Set the severity of one id in .editorconfig:

ini
[*.cs]
dotnet_diagnostic.VM1201.severity = none

A [*.cs] section applies only to diagnostics reported in C# files. The language pack diagnostics are reported at the JSON file, and VM5001, VM5002, VM5004 and VM6001 have no location, so set their severity in a .globalconfig file, or list them in <NoWarn>:

ini
is_global = true
dotnet_diagnostic.VM4006.severity = warning

To silence one declaration, put #pragma warning disable VM1201 before it and #pragma warning restore VM1201 after it.

Every id is in the category ValidationModules.Usage, but a category-wide rule such as dotnet_analyzer_diagnostic.category-ValidationModules.Usage.severity reaches only VM5003. The source generator reports every other id, and category rules apply only to analyzers. Set the severity of each id separately, or list it in <NoWarn>.

Many errors mean the generator dropped the constraint it describes. Silencing such an error gives a build that passes without that check. Fix the declaration instead.

Constraint attributes ​

VM1001 ​

Severity: Error

A string constraint is on a property that is not a string. This covers [StringLength], [Pattern], [EmailAddress], [Phone], [Url], [CreditCard], [Base64String], [FileExtensions] and the DataAnnotations [RegularExpression]. [Url] also accepts a Uri. The constraint is dropped. To check the elements of a collection of strings, use Each in a rules class.

VM1002 ​

Severity: Error

[ItemCount] is on a property that is not a collection. Use [StringLength] for a string or [Range] for a number.

VM1003 ​

Severity: Error

[Range] is on a type that has no ordering, such as string, bool or an enum. Use [StringLength] for the length of a string, and [EnumDefined] or [AllowedValues] for an enum.

VM1004 ​

Severity: Error

[MultipleOf] is on a property that is not a number, or a rules-class MultipleOf chain is on a number the check cannot divide, such as nint. The check reads integral, decimal, double and float values.

VM1005 ​

Severity: Error

[UniqueItems] is on a property that is not a collection.

VM1006 ​

Severity: Error

[EnumDefined] is on a property that is not an enum, or on an enum that declares no members.

VM1007 ​

Severity: Error

A constrained property has no getter that the validator can call, for example a private getter. The property is skipped. Make the getter public or internal.

VM1008 ​

Severity: Warning

A constraint is on a positional record parameter without the property: target. It applies to the constructor parameter and has no effect. Write [property: Required].

VM1009 ​

Severity: Warning

A property declared with new hides a base property that has constraints. The base property's constraints no longer apply, because the most derived declaration supplies all of them. Repeat the constraints you still want on the new property, or rename one of the two.

VM1010 ​

Severity: Error

A generic type declares constraints. Its validator could not be registered without MakeGenericType, so none is generated. Declare the constraints on a closed type, or validate the generic type's payload and leave the generic type without constraints.

VM1011 ​

Severity: Warning

A constraint is on a field or a static property. The generator reads instance properties only, so the constraint is never evaluated. Declare the member as an instance property, as the message shows.

VM1013 ​

Severity: Error

Two types in one namespace would get generated classes with the same name. A class generated for a type declared inside another type carries the containing types' names, joined with underscores. A nested Order.Item and a top-level Order_Item therefore both get the validator Order_ItemValidator, and neither validator is generated. A validator that nests either type is not generated either. The body of a rules class is generated into a class named after the rules class with _Rules appended. A nested rules class Order.ItemRules and a top-level Order_ItemRules therefore both get Order_ItemRules_Rules, and neither rules class is compiled. The fragments a type declares are generated into a class named after the type with _Fragments appended. A nested Order.Shared and a top-level Order_Shared that both declare fragments therefore both get Order_Shared_Fragments. Neither is generated, and a rules class that calls a fragment in either is left out with its validator. Rename one of the two types, or move it to another namespace.

VM1014 ​

Severity: Warning

A constraint is on an indexer. An indexer takes an argument and the validator has none to pass, so the constraint is never evaluated. Remove it. To check the values the indexer returns, expose the collection it reads from as an instance property, and check its elements with [ValidateNested] or with Each in a rules class.

VM1101 ​

Severity: Error

No value can satisfy a constraint's bounds, so the constraint can never pass. That is a minimum greater than its maximum on [StringLength], [ItemCount] or [Range], or equal [Range] bounds with ExclusiveMin or ExclusiveMax set. The DataAnnotations [StringLength], [Length] and [Range] are checked too. So are Range, Length and Count in a rules class, chained or not, when both bounds are constants. [Range] bounds are compared as the property's type, so date bounds compare as instants. The positional argument of [StringLength] is the maximum, and the first argument of [ItemCount] is the minimum.

VM1102 ​

Severity: Warning

[Range] sets neither a minimum nor a maximum, so it can never fail. The constraint is dropped. Set Min, Max or both.

VM1103 ​

Severity: Error

A [Range] bound does not parse as the property's type, for example a date on an int, or "25:00:00" on a TimeOnly. The constraint is dropped.

VM1104 ​

Severity: Error

The divisor of [MultipleOf], or a constant divisor of rules.MultipleOf, is zero or negative. The constraint is dropped.

VM1105 ​

Severity: Error

The [MultipleOf] divisor does not fit the property's type, for example 2.5 on an int. The constraint is dropped.

VM1106 ​

Severity: Error

The expression in [Pattern] or [RegularExpression] is not a valid regular expression. The message includes the parser's explanation. The constraint is dropped.

VM1107 ​

Severity: Error

The member named by [Pattern(typeof(T), "Member")] cannot be used. The message says whether it does not exist, is not static, is not accessible, takes parameters, or is not a Regex. Point it at a static Regex method, property or field that is internal or public.

VM1201 ​

Severity: Warning

[Required] is on a non-nullable value type, such as int or Guid. The property always has a value, so the constraint can never fail and is dropped. Make the property nullable, or check its value with [Range] or [EnumDefined]. ImmutableArray<T> is not reported, because a default array reads as missing.

VM1202 ​

Severity: Warning

[UniqueItems] is on a collection whose elements compare by reference, so two elements with the same contents are both accepted. Make the element type a record, override Equals, or implement IEquatable<T>.

VM1203 ​

Severity: Warning

[AllowedValues] sets Comparison on a property that is not a string. Comparison applies only to strings, so the values compare as they would without it. Remove Comparison.

VM1301 ​

Severity: Error or warning

An inline [Pattern("...")], or a DataAnnotations [RegularExpression], is in a project whose pattern policy rejects it. By default that is a project with PublishAot or IsAotCompatible set to true, and the diagnostic is an error that drops the constraint. Both compile to an expression parsed at run time, which adds the regular expression parser and interpreter to a Native AOT binary, about 360 KB. Options or a match timeout adds about 490 KB more. The message gives that figure when the pattern has either. Declare the expression with [GeneratedRegex] and point at it with [Pattern(typeof(T), nameof(T.Member))], or set ValidationModules_PatternPolicy to Allow. For [RegularExpression], the message prints the expression anchored and made optional, because that attribute matches the whole value and passes an empty string. It also prints the attribute's match timeout, which is 2000 milliseconds unless MatchTimeoutInMilliseconds sets another. See Patterns.

VM1302 ​

Severity: Warning

An inline [Pattern] sets Options to include RegexOptions.Compiled. The generator removes it, because compiling the expression would emit code at run time, and the inline pattern is interpreted. Remove it from Options. For a matcher compiled at build time, declare the expression with [GeneratedRegex] and point at it with [Pattern(typeof(T), nameof(T.Member))].

VM1303 ​

Severity: Warning

[Pattern(typeof(T), nameof(T.Member))] sets Options or MatchTimeoutMilliseconds. The referenced regex was built with its own options and timeout, so the setting has no effect. Remove it and declare it on the [GeneratedRegex] instead. The message prints that declaration, merged into the member's own [GeneratedRegex] when it has one. RegexOptions.Compiled is reported here rather than as VM1302, and only needs removing.

VM1304 ​

Severity: Warning

An inline [Pattern] sets MatchTimeoutMilliseconds, or a DataAnnotations [RegularExpression] sets MatchTimeoutInMilliseconds, to a value the Regex constructor rejects. That is zero, a negative number other than -1, or int.MaxValue. The generator ignores the value, so the validator does not throw when it loads. The [Pattern] then has no timeout, and the [RegularExpression] keeps the DataAnnotations default of 2000 milliseconds. Set a value from 1 to 2147483646 milliseconds. For no timeout, remove MatchTimeoutMilliseconds from [Pattern], or set MatchTimeoutInMilliseconds to -1 on [RegularExpression].

VM1401 ​

Severity: Error

When or Unless names a member the model does not declare. Use nameof to catch misspellings.

VM1402 ​

Severity: Error

The member named by When or Unless is not a bool property, a parameterless method that returns bool, or a static method that takes the model and returns bool.

VM1403 ​

Severity: Error

A constraint sets both When and Unless. Write two constraints, or one condition that combines both.

VM1501 ​

Severity: Warning

A descent reaches a type that declares no rules, so there is nothing to validate and the descent is dropped. The descent comes from [ValidateNested], or from Nested or Each in a rules class, and the message names which. DataAnnotations attributes that produce no check, such as [Display] and [Key], are not rules. Give the type constraints, a rules class or [GenerateValidator], or remove the descent.

VM1502 ​

Severity: Warning

A descent reaches a type that no validator can be generated for, such as a list of lists or a list of nullable values. The descent comes from [ValidateNested], or from Nested or Each in a rules class. The descent is dropped. Wrap the inner collection in a type that has its own rules.

VM1503 ​

Severity: Warning

The type of a [ValidateNested] property is not sealed, so a value of a derived type may reach it, and no Polymorphism is given. Seal the type, or pass Polymorphism.DeclaredOnly, Polymorphism.CompileTime or Polymorphism.Runtime. See Subtypes. Nested and Each in a rules class report VM3111 instead.

VM1504 ​

Severity: Error

Polymorphism.Runtime is on a sealed type or a value type, whose actual type can never differ from its declared type. Use Polymorphism.DeclaredOnly. The mode comes from [ValidateNested], or from Nested or Each in a rules class, where the error is reported at the call.

VM1505 ​

Severity: Warning

A descent reaches a type declared in another assembly, and this project can reach no validator for it. The other assembly has no accessible <Type>Validator, and no rules class in this project targets the type. This is the case for a framework type such as StringBuilder, and for a type from an assembly that does not use this generator. The descent is dropped. Declare an IValidationRulesFor<T> for the type in this project, or remove the descent.

VM1601 ​

Severity: Error

A CustomConstraintAttribute subclass cannot be compiled. The message gives the reason: no public static bool IsValid that takes the value, IsValid parameters that do not match the constructor's, a constructor argument that is not a constant, or a property set at the point of use that IsValid cannot receive. See Custom constraints.

VM1602 ​

Severity: Error

An attribute that implements IConstraintFor<T> cannot be compiled. The message gives the reason: it also derives from CustomConstraintAttribute, it is generic, none of its IConstraintFor<T> interfaces accepts the property's type, more than one does, or an argument is not a constant.

VM1603 ​

Severity: Info

The attribute class is marked [PerValidationInstance], so every check creates a new instance of it.

DataAnnotations ​

VM2001 ​

Severity: Info

ValidationModules_DataAnnotations is set to Ignore, and the generator ignores this DataAnnotations attribute. Another validation system may still enforce it.

VM2002 ​

Severity: Info

A custom ValidationAttribute runs its own code. The generator creates it once and calls it with a DataAnnotations ValidationContext for each check. It is a warning, and the attribute is dropped, when the attribute's arguments cannot be written into generated code.

VM2003 ​

Severity: Warning

[Compare] compares two members and is not compiled. Use Ensure in a rules class.

VM2004 ​

Severity: Info

A DataAnnotations format attribute, such as [EmailAddress] or [Url], is compiled with the DataAnnotations rule. The message states the rule. Use [Pattern] for a stricter one.

VM2005 ​

Severity: Error

[MinLength], [MaxLength] or [Length] is on a property that is neither a string nor a collection.

VM2006 ​

Severity: Info

The model implements IValidatableObject. Its Validate method runs after every other rule on the type, and only when those rules reported no error. Warnings do not stop it, and neither do errors on other objects in the same pass.

VM2007 ​

Severity: Warning

[EnumDataType] is not compiled. Type the property as the enum and use [EnumDefined].

VM2008 ​

Severity: Error

[CustomValidation] names a method that cannot be called. The method must be public and static, take the value and optionally a ValidationContext, and return a DataAnnotations ValidationResult. The message gives the reason.

VM2009 ​

Severity: Warning

A custom ValidationAttribute sets ErrorMessageResourceType. DataAnnotations reads the resource with reflection, which trimming can break. Set ErrorMessage, or keep the resource type from being trimmed.

VM2010 ​

Severity: Info

A ValidationAttribute is on the class, so it validates the whole object. It runs after the property rules, and only when they reported no error, and before IValidatableObject.Validate. That is the order Validator.TryValidateObject uses. A [CustomValidation] on the class calls its method directly with the object as the value. Any other attribute is created once and called.

The attribute is reported where it is declared. One on a base class or an interface also applies to the classes that derive from it or implement it.

It is a warning, and the attribute is not enforced, when the attribute's arguments cannot be written into generated code. Move the rule into IValidatableObject.Validate, or into an Ensure in a rules class.

VM2011 ​

Severity: Error

A DataAnnotations attribute sets ErrorMessageResourceType and ErrorMessageResourceName, and the name is not a static string property that the generated validator can read. The generated code reads the property directly, without reflection. The message gives the reason: no member has the name, or the member is not static, not a string, or not accessible. An internal property is accessible only from its own assembly, so a resx file in a shared project needs the Public access modifier. Write the name with nameof, so the compiler checks it at the attribute: ErrorMessageResourceName = nameof(Messages.NameRequired). The constraint still compiles, with its default message.

Rules classes ​

VM3001 ​

Severity: Error

Describe contains something the generator cannot copy into the validator. The message names it. The cases are a try, lock, using or goto statement, a return with a value, an assignment to a member of x, Apply anywhere but the top level of Describe, Require chained after Each, Nested, Each or Apply inside a fragment, and a rule call that does not compile. The descents that are refused are Nested on a collection or a dictionary, and a second Nested or Each in the same chain as one before it. Use Each for a list, and [ValidateNested] on the property for a dictionary. See the rules API reference.

VM3002 ​

Severity: Error

The rules object is used in a way the generator cannot follow: stored in a variable, captured in a lambda, returned, or passed to a method that is not a fragment. The same error covers a fragment or As given something other than x.

VM3003 ​

Severity: Error

A rule is declared inside a loop or a local function. A local function also gives VM3002, and a rule inside a lambda gives VM3002 alone. Use Each for per-element rules, or report from the loop through rules.Context.

rules.Context inside a lambda, an anonymous method, a local function or a query expression gives VM3003 too. The generated code passes the context by reference, and none of those can capture it. Report from a foreach loop instead.

VM3004 ​

Severity: Error

The body of Describe uses a member that the generated class cannot reach, such as a private method or field of the rules class. Make it internal. A private const is allowed. The method passed to Pattern or Apply counts, so a [GeneratedRegex] method written private static partial is reported here.

VM3005 ​

Severity: Error

A fragment is declared in a referenced assembly. Fragments must be source in the same project. Share them through a shared project or a source-only package.

VM3006 ​

Severity: Error

Fragments call each other in a cycle. The message shows the cycle.

VM3007 ​

Severity: Error

A rule's value is not a member path on x, for example x.Name.Trim(), so the error has no field. Pass a member path, or give the field with field:.

VM3008 ​

Severity: Error

The argument to Pattern or Apply names no static method, so the generated code has nothing to call. Pass a method group, such as rules.Pattern(x.Sku, SkuPattern) or rules.Apply(Check). A lambda whose whole body calls one static method, such as () => SkuPattern(), is read as that method. A lambda that does anything else, a delegate stored in a field, and an instance method are reported here. The message gives the method-group form to write.

VM3009 ​

Severity: Error

A generic fragment is called with a type argument that its generated expansion cannot name. The generator expands the fragment once for each set of type arguments, and writes each use of a type parameter, such as typeof(T), as the type it stands for. An anonymous type has no name, and a private or protected type cannot be reached from the generated code. Pass a value of a named type, such as a record, or make the type internal.

VM3101 ​

Severity: Error

Require is applied to a non-nullable value type, which can never be missing. Use a range rule, or make the property nullable. ImmutableArray<T> is not reported, because a default array reads as missing.

VM3102 ​

Severity: Error

An Ensure condition reads no member of x, so it has no field to report against. Read the member the rule is about, or pass field:.

VM3103 ​

Severity: Info

An Ensure without code: reports a code derived from its condition. The message states the code. Pass code: to keep the code fixed when the condition changes.

VM3104 ​

Severity: Warning

A rule unwraps a nullable member with .Value. The rule accepts the nullable directly, and the generator corrects the call. Remove .Value.

VM3105 ​

Severity: Error

As<TFacet> names an interface or base type that has no rules in this project. Give it constraint attributes or a rules class.

VM3106 ​

Severity: Warning

Nested or Each in a rules class descends into a property that already has [ValidateNested]. Both descents would run, and every error in the nested object would be reported twice. The rules-class descent is dropped. Remove it, or remove [ValidateNested] to keep the descent in the rules class.

VM3108 ​

Severity: Error

A value given to AllowedValues is not a compile-time constant, such as a static readonly field, a spread, or an array held in a field. The generator writes every value into the check and its message at build time. Declare the value const, write the values out in the call, or write the rule as an Ensure.

VM3109 ​

Severity: Warning

An AllowedValues in a rules class, or an [AllowedValues] attribute from either vocabulary, lists no values. An empty set checks nothing, so the rule is dropped. List the permitted values, or remove the rule. An empty [DeniedValues] is not reported, because it denies nothing.

VM3110 ​

Severity: Error

As<TFacet> names the type of x itself. The validator for that type would call itself until the stack overflows. The same applies to As<T> in a generic fragment, where T is the type of the fragment's subject. Remove the call, because the rules for the type already run, or name an interface or base type of x instead.

VM3111 ​

Severity: Warning

Nested or Each in a rules class descends into a type that is not sealed, and passes no Polymorphism. A value of a more derived type may reach it, and the descent runs only the validators for the declared type, so rules declared for the more derived type do not run. To run the rules for the actual type, pass Polymorphism.CompileTime or Polymorphism.Runtime, as in rules.Nested(x.Pet, Polymorphism.CompileTime). The message prints the call with the argument added. A class that nothing derives from can be sealed instead. To keep checking the declared type only, pass Polymorphism.DeclaredOnly. See Subtypes.

Language packs ​

These diagnostics point at the JSON file.

VM4001 ​

Severity: Error

The language pack is not valid JSON, or its culture is missing or is not a culture name. A culture name is spelled as CultureInfo.Name spells it, in letters and digits joined by hyphens, such as fr-CA. The file is skipped.

VM4002 ​

Severity: Warning

A key names a shape that does not exist, such as string_length.atmost. The message suggests the nearest shape. The entry is skipped.

VM4003 ​

Severity: Error

A template uses an argument number that its shape does not have. The entry is skipped.

VM4004 ​

Severity: Error

A key appears more than once. The entries after the first are skipped.

VM4005 ​

Severity: Warning

The culture in the file name differs from the culture in the file. The file's culture is used.

VM4006 ​

Severity: Info

The pack does not cover every shape. The message lists the missing keys. Messages for those shapes stay in English. To require complete packs, raise it to a warning or an error in a .globalconfig file, as shown under Changing a severity.

Generator and runtime ​

VM5001 ​

Severity: Error

The referenced ValidationModules.Runtime is older than the generated code requires, or it is missing. Reference ValidationModules.Runtime at the same version as ValidationModules.SourceGenerator. Other compile errors in generated files may appear with this one, so fix it first.

VM5002 ​

Severity: Error

The generator failed while writing code. The build fails so that a validator cannot go missing without notice. The message names the stage and the exception. A generated file that refers to the one that failed is left out too, so VM5002 is the only error. For example, a rules class that failed takes its type's validator with it, and the registration leaves that validator out. Please report it. Until it is fixed, change the construct the message names.

VM5003 ​

Severity: Warning

.Validate<T>() names a type in this project that has no constraints, no [GenerateValidator], no rules class and no hand-written validator. The endpoint throws when it is built, which in a default application happens on its first request. Add rules or [GenerateValidator]. When ValidationModules_DataAnnotations is Ignore, DataAnnotations attributes do not count as rules. When the rules come from another assembly, the warning does not apply. This is the one diagnostic that an analyzer reports rather than the generator.

VM5004 ​

Severity: Warning

A ValidationModules_* property in the project file has a value that the generator does not accept, so the generator uses the property's default. The message names the property, the value and the values it accepts. Case does not matter: snakecase is read as SnakeCase, but snake_case is not a value. See MSBuild properties.

Registration ​

VM6001 ​

Severity: Warning

The project declares more than one module entry point, marked [DependencyModule] or [HardenedModule], and the validators are registered into each of them. Remove the attribute from the classes that are not applications. When the project contains two applications on purpose, add VM6001 to <NoWarn>.

Ids before 1.0.0 ​

Before 1.0.0 the ids were numbered VM0001 to VM0108. A .editorconfig or <NoWarn> entry that uses an old id has no effect now. Replace it with the new id:

Before 1.0.0NowBefore 1.0.0Now
VM0001VM1001VM0065VM1103
VM0002VM1002VM0067VM2006
VM0003VM1003VM0068VM2007
VM0004VM1201VM0070VM3001
VM0006VM1106VM0071VM3007
VM0007VM1501VM0075VM3102
VM0008VM1101VM0079VM1010
VM0009VM1007VM0080VM2008
VM0010VM2001VM0081VM2009
VM0016VM1302VM0082VM1601
VM0017VM1301VM0083VM1602
VM0018VM1107VM0084VM1603
VM0021VM1004VM0085VM3005
VM0022VM1104VM0086VM3006
VM0023VM1105VM0087VM3002
VM0024VM1005VM0088VM3004
VM0025VM1202VM0089VM3003
VM0026VM1102VM0090VM3101
VM0027VM1006VM0091VM3105
VM0028VM1401VM0092VM3103
VM0029VM1402VM0093VM3104
VM0030VM1009VM0100VM4001
VM0031VM1503VM0101VM4002
VM0032VM1504VM0102VM4003
VM0033VM1403VM0103VM4004
VM0040VM5001VM0104VM4005
VM0051VM1008VM0105VM4006
VM0060VM2002VM0106VM1502
VM0061VM2003VM0107VM5002
VM0063VM2004VM0108VM5003
VM0064VM2005

VM6001 was added in 1.1.0, and VM1011, VM1013, VM1014, VM1203, VM1303, VM1304, VM1505, VM2010, VM2011, VM3008, VM3009, VM3106, VM3108, VM3109, VM3110, VM3111 and VM5004 were added in 1.2.0. None of them has an earlier id.

Released under the MIT License.