diff --git a/src/Components/Components/src/ComponentBase.cs b/src/Components/Components/src/ComponentBase.cs
index eefe8eb74655..12793096ec3c 100644
--- a/src/Components/Components/src/ComponentBase.cs
+++ b/src/Components/Components/src/ComponentBase.cs
@@ -42,6 +42,15 @@ public ComponentBase()
};
}
+ ///
+ /// Gets or sets a value that indicates whether there is a pending queued render.
+ ///
+ internal bool HasPendingQueuedRender
+ {
+ get => _hasPendingQueuedRender;
+ set => _hasPendingQueuedRender = value;
+ }
+
///
/// Gets the the component is running on.
///
diff --git a/src/Components/Components/src/RenderTree/Renderer.cs b/src/Components/Components/src/RenderTree/Renderer.cs
index ad0864443da4..99d2551db607 100644
--- a/src/Components/Components/src/RenderTree/Renderer.cs
+++ b/src/Components/Components/src/RenderTree/Renderer.cs
@@ -1176,6 +1176,13 @@ private void HandleExceptionViaErrorBoundary(Exception error, ComponentState? er
// making it render an empty fragment. Ensures that failed components don't continue to
// operate, which would be a whole new kind of edge case to support forever.
AddToRenderQueue(candidate.ComponentId, builder => { });
+ if (candidate.Component is ComponentBase componentBase)
+ {
+ // If the ErrorBoundary already handled one error and caught another, there can be a bug, where
+ // HasPendingQueuedRender is still true from the previous error handling render. Clear it to avoid
+ // rendering empty fragment and skipping the ErrorContent.
+ componentBase.HasPendingQueuedRender = false;
+ }
try
{
diff --git a/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs b/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs
index 80402e38bf54..a658ba834595 100644
--- a/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs
+++ b/src/Components/test/E2ETest/Tests/ErrorBoundaryTest.cs
@@ -114,6 +114,16 @@ public void CanHandleErrorsInlineInErrorBoundaryContent()
AssertGlobalErrorState(true);
}
+ [Fact]
+ public void CanHandleMultipleExceptionsForOnce()
+ {
+ var container = Browser.Exists(By.Id("throw-multiple-errors-foreach-test"));
+ container.FindElement(By.ClassName("throw-multiple-errors-foreach")).Click();
+ Browser.Collection(() => container.FindElements(By.ClassName("error-message")),
+ elem => Assert.Equal("There was an error.", elem.Text));
+ AssertGlobalErrorState(false);
+ }
+
[Fact]
public void CanHandleErrorsAfterDisposingComponent()
{
diff --git a/src/Components/test/testassets/BasicTestApp/ErrorBoundaryTest/ErrorBoundaryCases.razor b/src/Components/test/testassets/BasicTestApp/ErrorBoundaryTest/ErrorBoundaryCases.razor
index 6b1916019d06..c775385128c6 100644
--- a/src/Components/test/testassets/BasicTestApp/ErrorBoundaryTest/ErrorBoundaryCases.razor
+++ b/src/Components/test/testassets/BasicTestApp/ErrorBoundaryTest/ErrorBoundaryCases.razor
@@ -52,6 +52,26 @@
+
This shows that, if an ErrorBoundary itself fails while rendering its own ChildContent, then it can catch its own exception. But if the error comes from the error content, this triggers the "infinite error loop" detection logic and becomes fatal.