Skip to content

Commit f9234ea

Browse files
Fix RPC recipes errors being swallowed when in batch (#7226)
1 parent a311899 commit f9234ea

2 files changed

Lines changed: 64 additions & 1 deletion

File tree

rewrite-core/src/main/java/org/openrewrite/scheduling/RecipeRunCycle.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,14 @@ void clear() {
406406
response = rpc.batchVisit(originalBefore, ctx, rootCursor, batch.items);
407407
} catch (Throwable t) {
408408
if (!batch.recipeStacks.isEmpty()) {
409-
handleError(batch.recipeStacks.get(0).peek(), originalBefore, originalBefore, t);
409+
SourceFile beforeError = source;
410+
if (!(t instanceof RecipeRunException)) {
411+
source = Markup.error(source, t);
412+
}
413+
source = handleError(batch.recipeStacks.get(0).peek(), originalBefore, source, t);
414+
if (source != null && source != beforeError) {
415+
source = addRecipesThatMadeChanges(batch.recipeStacks.get(0), source);
416+
}
410417
}
411418
batch.clear();
412419
return source;

rewrite-core/src/test/java/org/openrewrite/rpc/RewriteRpcTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.junit.jupiter.api.Disabled;
2626
import org.junit.jupiter.api.Test;
2727
import org.openrewrite.*;
28+
import org.openrewrite.internal.InMemoryLargeSourceSet;
29+
import org.openrewrite.marker.Markup;
2830
import org.openrewrite.marker.Markers;
2931
import org.openrewrite.config.CompositeRecipe;
3032
import org.openrewrite.config.Environment;
@@ -413,6 +415,37 @@ void singleRpcRecipeNoBatch() {
413415
);
414416
}
415417

418+
/**
419+
* When an RPC batch visit throws an exception (e.g. a recipe visitor fails on the
420+
* remote side), the error must be visible: the source file should carry a Markup.Error
421+
* marker and the error should be recorded in the SourcesFileErrors data table.
422+
*/
423+
@Test
424+
void batchVisitExceptionProducesErrorMarker() {
425+
// given
426+
// Two consecutive same-RPC recipes are required for the batch path to kick in
427+
Recipe r1 = client.prepareRecipe("org.openrewrite.rpc.RewriteRpcTest$ThrowingRpcRecipe", Map.of());
428+
Recipe r2 = client.prepareRecipe("org.openrewrite.rpc.RewriteRpcTest$ThrowingRpcRecipe", Map.of());
429+
430+
var errors = new java.util.ArrayList<Throwable>();
431+
var ctx = new InMemoryExecutionContext(errors::add);
432+
var source = PlainText.builder().text("hello").sourcePath(Path.of("test.txt")).build();
433+
434+
// when
435+
RecipeRun run = new RecipeScheduler().scheduleRun(
436+
new CompositeRecipe(List.of(r1, r2)),
437+
new InMemoryLargeSourceSet(List.of(source)), ctx, 1, 1);
438+
439+
// then
440+
assertThat(errors).isNotEmpty();
441+
442+
List<Result> results = run.getChangeset().getAllResults();
443+
assertThat(results).isNotEmpty();
444+
assertThat(results.getFirst().getAfter().getMarkers().findFirst(Markup.Error.class))
445+
.describedAs("Source should carry Markup.Error so the failure is visible")
446+
.isPresent();
447+
}
448+
416449
@Test
417450
void getCursor() {
418451
var parent = new Cursor(null, Cursor.ROOT_VALUE);
@@ -432,6 +465,29 @@ public PlainText visitText(PlainText text, Integer p) {
432465
}
433466
}
434467

468+
@SuppressWarnings("unused")
469+
public static class ThrowingRpcRecipe extends Recipe {
470+
@Override
471+
public String getDisplayName() {
472+
return "Throwing RPC recipe";
473+
}
474+
475+
@Override
476+
public String getDescription() {
477+
return "Test recipe that throws during visit.";
478+
}
479+
480+
@Override
481+
public TreeVisitor<?, ExecutionContext> getVisitor() {
482+
return new PlainTextVisitor<>() {
483+
@Override
484+
public PlainText visitText(PlainText text, ExecutionContext ctx) {
485+
throw new RuntimeException("boom from RPC");
486+
}
487+
};
488+
}
489+
}
490+
435491
@SuppressWarnings("unused")
436492
static class RecipeWithRecipeList extends Recipe {
437493
@Override

0 commit comments

Comments
 (0)