Skip to content

Commit 157ca4e

Browse files
Let error boundaries catch their own inline errors
1 parent a1c3ccb commit 157ca4e

File tree

3 files changed

+27
-4
lines changed

3 files changed

+27
-4
lines changed

src/Components/Components/src/RenderTree/Renderer.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,7 @@ private bool TrySendExceptionToErrorBoundary(IComponent? errorSource, Exception
327327
// If it becomes problematic, we can maintain a lookup from component instance to ID.
328328
var componentState = _componentStateById.FirstOrDefault(kvp => kvp.Value.Component == errorSource).Value;
329329

330-
// Find the closest IErrorBoundary, if any. Start looking at the first parent, to ensure
331-
// that an error boundary component doesn't try to handle its own errors, as that could
332-
// be an infinite loop.
333-
componentState = componentState?.ParentComponentState;
330+
// Find the closest IErrorBoundary, if any
334331
while (componentState is not null)
335332
{
336333
if (componentState.Component is IErrorBoundary errorBoundary)

src/Components/Samples/BlazorServerApp/Pages/ErrorMaker.razor

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,22 @@
4545
<ErrorCausingCounter />
4646
</ErrorIgnorer>
4747
</fieldset>
48+
49+
<fieldset>
50+
<legend>Exception inline in error boundary markup</legend>
51+
<ErrorBoundary>
52+
<ChildContent>
53+
@if (throwInline) { throw new InvalidTimeZoneException("Inline exception"); }
54+
<p>Hello!</p>
55+
</ChildContent>
56+
<ErrorContent>
57+
@if (throwInErrorContent) { throw new InvalidTimeZoneException("Inline exception in error content"); }
58+
<p>There was an error: @context</p>
59+
</ErrorContent>
60+
</ErrorBoundary>
61+
<button @onclick="@(() => throwInline = true)">Throw in child content</button>
62+
<button @onclick="@(() => throwInErrorContent = true)">Throw in error content</button>
63+
</fieldset>
4864
</fieldset>
4965

5066
@code {
@@ -98,6 +114,8 @@
98114
private bool throwInOnAfterRender;
99115
private bool throwInOnAfterRenderAsync;
100116
private bool throwWhileRendering;
117+
private bool throwInline;
118+
private bool throwInErrorContent;
101119

102120
void EventHandlerErrorSync()
103121
=> throw new InvalidTimeZoneException("Synchronous error from event handler");

src/Components/Web/src/Web/ErrorBoundary.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Runtime.ExceptionServices;
56
using System.Threading.Tasks;
67
using Microsoft.AspNetCore.Components.Rendering;
78

@@ -26,6 +27,13 @@ public sealed class ErrorBoundary : ComponentBase, IErrorBoundary
2627

2728
public void HandleException(Exception exception)
2829
{
30+
if (_currentException is not null)
31+
{
32+
// If there's an error while we're already displaying error content, then it's the
33+
// error content that's failing. Avoid the risk of an infinite error rendering loop.
34+
ExceptionDispatchInfo.Capture(exception).Throw();
35+
}
36+
2937
_ = ErrorBoundaryLogger!.LogErrorAsync(exception, clientOnly: false);
3038

3139
_currentException = exception;

0 commit comments

Comments
 (0)