ASP.net Core API : ValidationVisitor exceeded the maximum configured validation depth '32'

Viewed 3044

AS am running an ASP.Net core web API from docker container , it throws a validation error :

System.InvalidOperationException: ValidationVisitor exceeded the maximum configured validation depth '32' when validating type 'ClassName'. This may indicate a very deep or infinitely recursive object graph. Consider modifying 'MvcOptions.MaxValidationDepth' or suppressing validation on the model type.

The only place I could find a discussion about this issue is in here , where it seems that a fix has been provided on the latest version of ASP.net core . I updated my .net core version to the latest , but still facing the same issue.
Here is the code of the class where the validation is causing the issue :

        [Required]
        [Range(1, long.MaxValue)]
        public long Id { get; set; }
        [Required(AllowEmptyStrings = false)]
        [StringLength(1000)]
        public string Name { get; set; }
        [Required(AllowEmptyStrings = false)]
        [StringLength(200)]
        public string Category { get; set; }
        [Required(AllowEmptyStrings = false)]
        [StringLength(13)]
        public string Division { get; set; }

Important : Am the only one facing the issue , as the rest of my team is running the project successfully , any help is highly appreciated.

6 Answers

It is a bug in Asp.Net Core ModelBinding Validation affecting MVC and Web Api https://github.com/dotnet/aspnetcore/issues/13778

One workaround is to increase MaxModelValidationErrors in Startup.ConfigureServices:

services.AddMvc()
    .AddMvcOptions(options => {
    options.MaxModelValidationErrors = 999999;
})

Increasing MaxModelValidationErrors didn't fix things for me, I had to change a different value (MaxValidationDepth) to get things to work. Wanted to add it here in case anyone had the same problem as I did.

.AddMvcOptions(options =>
        {
            options.MaxValidationDepth = 999;
        });

You could increase MaxModelValidationErrors in Startup.ConfigureServices:

services.AddControllers(options =>
{
    options.MaxModelValidationErrors = 999999;
});

I cannot recommend SuppressChildValidationMetadataProvider anymore.

But you can use this attribute:

using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
[ValidateNever]
public List<ToNotValidateSet> ToNotValidateSet { get; set; }

...on all the virtual properties that should not get validated.

I am in the process of upgrading a project to .NET 6 and ran into this error message. The above solutions did not fix it. The validation error was referring to a virtual Subproperty and a validated something that I did not even want to be validated at this point.

This fixed it for me:

services.AddMvc(option => {
    //fix: max validation depth error in TryValidateModel(model) since .NET6
    option.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(MyVirtualSubpropertyClassThatShouldNotBeValidated)));
});

Disclaimer: I am well aware that this is a quickfix and potentially has sideeffects. Also I cannot spend a week on properly fixing it and just want a working version for now.

The issue for me was that I was using lazy loading with Entity Framework Core.

The validator would traverse the model recursively and fetch all bound properties through the lazy loading. 

Since my model has a lot of loops it would validate it indefinitely but by default the validator stops at recursion depth level 32 and throws an exception.

You can increase the number of levels by setting the MaxValidationDepth but it would just work longer yielding the same result.  

It would be nice if we could tell the validator to just stop (without throwing the error) at some level but there is no such option.

In fact all I wanted to validate was just the first level properties of my model without going any deeper.

Getting detached models in the first place would fix the problem but my API would not allow it.

So here is the workaround I'm using now:

public T GetDetachedModel<T>(T model) where T : new() 
    {
        T detachedModel = new T();

        foreach (var p in typeof(T).GetProperties())
        {
            if (!(p.GetAccessors()?[0].IsVirtual ?? false))
            {
                p.SetValue(detachedModel, p.GetValue(model, null));
            }
        }

        return detachedModel;
    }

Lazy loading requires all mapped properties to be declared virtual. We exploit that fact by creating the new object and member copying non virtual properties.

The "detached" model can be used in place of the original one.

Related