diff --git a/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs b/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs index 74e5a5bf932..047918b5c92 100644 --- a/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs +++ b/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs @@ -1342,6 +1342,41 @@ public void EngineShouldHandleExceptionsFromCachePluginViaGraphBuild(ErrorLocati } } + [Fact] + public async Task CriticalExceptionFromGraphBuildCompletesSubmission() + { + const string exceptionMessage = "Critical exception from graph build"; + + var project = _env.CreateFile( + "1.proj", + """ + + + + """); + var graph = new ProjectGraph(project.Path); + var cache = new DelegatingMockCache( + (_, _, _) => throw new InternalErrorException(exceptionMessage)); + var buildParameters = new BuildParameters + { + ProjectCacheDescriptor = ProjectCacheDescriptor.FromInstance(cache), + ShutdownInProcNodeOnBuildFinish = true + }; + var requestData = new GraphBuildRequestData(graph, ["Build"]); + + Task buildTask = Task.Run( + () => + { + using var buildManager = new BuildManager(); + return Should.Throw(() => buildManager.Build(buildParameters, requestData)); + }); + + Task completedTask = await Task.WhenAny(buildTask, Task.Delay(TimeSpan.FromSeconds(10))); + + completedTask.ShouldBeSameAs(buildTask, "Graph builds should complete after a critical exception."); + (await buildTask).Message.ShouldContain(exceptionMessage); + } + [Fact] public void EndBuildShouldGetCalledOnceWhenItThrowsExceptionsFromGraphBuilds() { diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 4b69ec5a6e2..7753a5fafd2 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -1772,9 +1772,9 @@ private void ExecuteSubmission(GraphBuildSubmission submission) { ExecuteGraphBuildScheduler(submission); } - catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) + catch (Exception ex) { - HandleSubmissionException(submission, ex); + HandleGraphSubmissionException(submission, ex); } }, _executionCancellationTokenSource!.Token, @@ -1783,13 +1783,25 @@ private void ExecuteSubmission(GraphBuildSubmission submission) } } // The handling of submission exception needs to be done outside of the lock - catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) + catch (Exception ex) { - HandleSubmissionException(submission, ex); + HandleGraphSubmissionException(submission, ex); throw; } } + private void HandleGraphSubmissionException(GraphBuildSubmission submission, Exception ex) + { + if (ExceptionHandling.IsCriticalException(ex)) + { + OnThreadException(ex); + } + else + { + HandleSubmissionException(submission, ex); + } + } + /// /// Creates the traversal and metaproject instances necessary to represent the solution and populates new configurations with them. ///