dotnet/aspnetcore · critical · ArgumentException
Invalid layout type: {layoutType.FullName} does not implemen
Error message
Invalid layout type: {layoutType.FullName} does not implement {typeof(IComponent).FullName}. What it means
Thrown by the LayoutAttribute constructor when the type passed as the layout does not implement IComponent. Blazor requires every layout to be a renderable component, so the attribute validates this contract at construction time. The check uses typeof(IComponent).IsAssignableFrom(layoutType), so any non-component type (e.g. a plain class or interface) triggers it.
Source
Thrown at src/Components/Components/src/LayoutAttribute.cs:25
namespace Microsoft.AspNetCore.Components;
/// <summary>
/// Indicates that the associated component type uses a specified layout.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class LayoutAttribute : Attribute
{
/// <summary>
/// Constructs an instance of <see cref="LayoutAttribute"/>.
/// </summary>
/// <param name="layoutType">The type of the layout.</param>
public LayoutAttribute([DynamicallyAccessedMembers(Component)] Type layoutType)
{
LayoutType = layoutType ?? throw new ArgumentNullException(nameof(layoutType));
if (!typeof(IComponent).IsAssignableFrom(layoutType))
{
throw new ArgumentException($"Invalid layout type: {layoutType.FullName} " +
$"does not implement {typeof(IComponent).FullName}.");
}
// Note that we can't validate its acceptance of a 'Body' parameter at this stage,
// because the contract doesn't force them to be known statically. However it will
// be a runtime error if the referenced component type rejects the 'Body' parameter
// when it gets used.
}
/// <summary>
/// The type of the layout. The type must implement <see cref="IComponent"/>
/// and must accept a parameter with the name 'Body'.
/// </summary>
[DynamicallyAccessedMembers(Component)]
public Type LayoutType { get; private set; }
}
View on GitHub (pinned to 294cab2f9b)
Solutions
- Make the referenced layout type implement IComponent, typically by inheriting from LayoutComponentBase or ComponentBase.
- Verify the type passed to [Layout(...)] is the component you intend (check the using/namespace resolution).
- Add a [Body] RenderFragment parameter to the layout so it functions as a real layout.
Example fix
// before
[Layout(typeof(MainLayoutShell))] // MainLayoutShell : UserControl, not a component
public class MyPage : ComponentBase { }
// after
[Layout(typeof(MainLayout))] // MainLayout : LayoutComponentBase
public class MyPage : ComponentBase { } Defensive patterns
Strategy: validation
Validate before calling
static bool IsValidLayout(Type t) => t != null && typeof(IComponent).IsAssignableFrom(t);
// before decorating or resolving:
if (!IsValidLayout(candidateLayoutType)) throw new InvalidOperationException($"{candidateLayoutType} is not a valid layout component."); Type guard
static bool IsLayoutComponent(Type t) => typeof(IComponent).IsAssignableFrom(t);
Try / catch
try { var attr = new LayoutAttribute(layoutType); }
catch (ArgumentException ex) when (ex.Message.Contains("does not implement"))
{
logger.LogError(ex, "Layout type invalid");
throw;
} Prevention
- Always derive layouts from LayoutComponentBase.
- Add a unit test asserting [Layout]-referenced types implement IComponent.
- Use nameof/typed references instead of typeof(string) lookups for layouts.
When it happens
Trigger: Decorating a class with [Layout(typeof(SomeNonComponentType))] where SomeNonComponentType does not implement IComponent; passing a Type instance to LayoutAttribute whose hierarchy does not include IComponent; referencing a layout type that was refactored away from inheriting ComponentBase/IComponent.
Common situations: Creating a custom layout that mistakenly inherits from a non-component base class; copy-pasting a layout reference and forgetting to inherit ComponentBase; Razor @layout directive resolving to a helper/utility class rather than a component; trimming/AOT scenarios where the type metadata is incomplete.
Related errors
- EqualTo validator requires a non-empty "other" parameter.
- FileExtensions validator requires a non-empty "extensions" p
- Range validator requires at least one of "min" or "max" para
- regex validator requires a non-empty "pattern" parameter.
- length/minlength/maxlength validator requires at least one o
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/8bf7b2395098dbe6.
Report an issue: GitHub.